zoukankan      html  css  js  c++  java
  • 关于异步编程中的bind(this)

    异步编程中的.bind(this)方法解决了异步执行后this指针指向全局函数的问题,主要可以通过以下两个场景加以说明:
    (本文所用例子基于React场景;为简便起见,仅在第一个例子中展示完整HTML代码,随后的例子中只展示主要的差别部分)

    1.React中使用setInterval()来渐变字体颜色:

    <!DOCTYPE html>
      <head>
        <script src="../build/react.js"></script>
        <script src="../build/react-dom.js"></script>
        <script src="../build/browser.min.js"></script>
      </head>
      <body>
       <div id="example">
       </div>
       <script type="text/babel">
        var Hello = React.createClass({
            getInitialState: function () {
              return {
                opacity: 1.0
              };
            },
            componentDidMount: function () {
              this.timer = setInterval(function () {
                var opacity = this.state.opacity;
                opacity -= .05;
                if (opacity < 0.1) {
                  opacity = 1.0;
                }
                this.setState({
                  opacity: opacity
                });
              }.bind(this), 100);
            },
            render: function () {
              return (
                <div style={{opacity: this.state.opacity}}>
                  Hello {this.props.name}
                </div>
              );
            }
          });
          ReactDOM.render(
            <Hello name="world"/>,
            document.getElementById('example')
          );
       </script>
      </body>
     </html>

    注意setinterval()方法中,回调函数一定要加.bind(this)方法,原因是:在setInterval()中定义的回调函数,是在同步代码执行完后,随着事件触发来异步执行的,此时函数的上下文Context已经由定义该函数的Script文件变为全局变量,如果不通过bind(this)来指定由组件实例作为上下文的话,回调函数中的this会指向全局变量中的Window变量,显然不是我们想要的结果。

    2.React中使用Ajax来更新组件:

    var UserGist = React.createClass({
      getInitialState: function() {
        return {
          username: '',
          lastGistUrl: ''
        };
      },
      componentDidMount: function() {
        $.get(this.props.source, function(result) {
          var lastGist = result[0];
          if (this.isMounted()) {
            this.setState({
              username: lastGist.owner.login,
              lastGistUrl: lastGist.html_url
            });
          }
        }.bind(this));
      },
      render: function() {
        return (
          <div>
          {this.state.username}'s last gist is <a href={this.state.lastGistUrl}>here</a>.
          </div>
        );
      }
    });
    ReactDOM.render(
      <UserGist source="https://api.github.com/users/octocat/gists" />,
      document.getElementById('example')
    );
    

    注意:在Ajax的回调函数中存在着同样的问题,若不通过.bind(this)指定示例作为上下文的话,当回调函数执行时上下文Context会被设置为全局变量,这时候this就会指向Window变量。

    3.React中使用Promise时:

    (jQuery从1.5版本开始$.ajax()及相关函数的返回对象便实现了Promise接口)

    var RepoList = React.createClass({
      getInitialState: function() {
        return {
          loading: true,
          error: null,
          data: null
        };
      },
      componentDidMount() {
        this.props.promise.then(
          value => this.setState({loading: false, data: value}),
          error => this.setState({loading: false, error: error}));
      },
      render: function() {
        if (this.state.loading) {
          return <span>Loading...</span>;
        }
        else if (this.state.error !== null) {
          return <span>Error: {this.state.error.message}</span>;
        }
        else {
          var repos = this.state.data.items;
          var repoList = repos.map(function (repo, index) {
            return (
          <li key={index}><a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}</li>
            );
          });
          return (
            <main>
              <h1>Most Popular JavaScript Projects in Github</h1>
              <ol>{repoList}</ol>
            </main>
          );
        }
      }
    });
    ReactDOM.render(
              <RepoList promise={$.getJSON('https://api.github.com/search/repositories?q=javascript&sort=stars')} />,
      document.getElementById('example')
    );
    

    注意:

    this.props.promise.then(value=>this.setState({loading:false,data:value}),error=>this.setState({loading:false,error:error}))
    

    中使用了箭头函数,这是ES6的一个语法糖,箭头函数中的this自动绑定到定义时的上下文Context,相当于:

    var ctx=this;
    componentDidMount(){
    this.props.promise.then(function(value){ctx.setState({loading:false,data:value});},function(error){ctx.setState({loading:false,error:error});})
    }
    

    可以与下面的代码比较:

    componentDidMount(){
    this.props.promise.then(function(value){this.setState({loading:false,data:value});},function(error){this.setState({loading:false,error:error});})
    }
    

    此代码不能进行加载,因为当Promise状态改变执行回调函数时,回调函数的上下文已经编程全局变量,this指向的不是该实例,而是Window变量。

    componentDidMount(){
    this.props.promise.then(function(value){this.setState({loading:false,data:value});}.bind(this),function(error){this.setState({loading:false,error:error});}.bind(this))
    }
    

    若将代码修改为上面这种,则可以正常运行。


    总结:

    1.异步回调函数的Context在未修改的情况下为全局变量,不是定义异步函数的Script,此结论对于Promise.then()中定义的回调函数同样使用,故而对于需要访问当前上下文的情况时,可以通过.bind(this)来指定上下文;
    2.=>箭头函数中this自带语法糖,绑定到定义函数时的上下文,故而Promise中建议使用箭头函数,以免造成this指向的混乱;
    3.类似的异步调用的情况除了Ajax和setTimeout类方法之外,还包括指定DOM元素对于某事件的回调函数时,此时的this会自动指向该DOM元素。




    来源:简书 http://events.jianshu.io/p/d7bde9560c65

    人最大的问题是总是带着成见去看待人和事!
  • 相关阅读:
    Vue3.0官方文档
    简单实现Vue的双向绑定原理
    小程序使用weapp-qrcode二维码插件,宽高自适应解决方法
    小程序判断ios还是android
    手写实现bind
    手写实现call,apply函数
    React onClick点击事件传参三种写法
    zynq 中断
    zynq_ps端点亮led灯代码
    突然发现自己的很多博客无法显示图片,人都傻了,于是就整理了一早上,全部换成了markdown格式,就好了,希望博客的时间不会对大家造成困扰!!!
  • 原文地址:https://www.cnblogs.com/liuyanhang/p/15251754.html
Copyright © 2011-2022 走看看