【隨筆】關(guān)于異步編程中的bind(this)

異步編程中的.bind(this)方法解決了異步執(zhí)行后this指針指向全局函數(shù)的問題哪审,主要可以通過以下兩個(gè)場(chǎng)景加以說明:
(本文所用例子基于React場(chǎng)景;為簡(jiǎn)便起見数尿,僅在第一個(gè)例子中展示完整HTML代碼被冒,隨后的例子中只展示主要的差別部分)

1.React中使用setInterval()來(lái)漸變字體顏色:

<!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()方法中,回調(diào)函數(shù)一定要加.bind(this)方法蠕搜,原因是:在setInterval()中定義的回調(diào)函數(shù)怎茫,是在同步代碼執(zhí)行完后,隨著事件觸發(fā)來(lái)異步執(zhí)行的妓灌,此時(shí)函數(shù)的上下文Context已經(jīng)由定義該函數(shù)的Script文件變?yōu)槿肿兞抗旄颍绻煌ㄟ^bind(this)來(lái)指定由組件實(shí)例作為上下文的話,回調(diào)函數(shù)中的this會(huì)指向全局變量中的Window變量虫埂,顯然不是我們想要的結(jié)果祥山。

2.React中使用Ajax來(lái)更新組件:

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的回調(diào)函數(shù)中存在著同樣的問題,若不通過.bind(this)指定示例作為上下文的話掉伏,當(dāng)回調(diào)函數(shù)執(zhí)行時(shí)上下文Context會(huì)被設(shè)置為全局變量缝呕,這時(shí)候this就會(huì)指向Window變量澳窑。

3.React中使用Promise時(shí):

(jQuery從1.5版本開始$.ajax()及相關(guān)函數(shù)的返回對(duì)象便實(shí)現(xiàn)了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}))

中使用了箭頭函數(shù),這是ES6的一個(gè)語(yǔ)法糖供常,箭頭函數(shù)中的this自動(dòng)綁定到定義時(shí)的上下文Context摊聋,相當(dāng)于:

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});})
}

此代碼不能進(jìn)行加載,因?yàn)楫?dāng)Promise狀態(tài)改變執(zhí)行回調(diào)函數(shù)時(shí)栈暇,回調(diào)函數(shù)的上下文已經(jīng)編程全局變量栗精,this指向的不是該實(shí)例,而是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))
}

若將代碼修改為上面這種,則可以正常運(yùn)行或舞。


總結(jié):

1.異步回調(diào)函數(shù)的Context在未修改的情況下為全局變量舷蒲,不是定義異步函數(shù)的Script,此結(jié)論對(duì)于Promise.then()中定義的回調(diào)函數(shù)同樣使用,故而對(duì)于需要訪問當(dāng)前上下文的情況時(shí),可以通過.bind(this)來(lái)指定上下文;
2.=>箭頭函數(shù)中this自帶語(yǔ)法糖原献,綁定到定義函數(shù)時(shí)的上下文,故而Promise中建議使用箭頭函數(shù)埂淮,以免造成this指向的混亂姑隅;
3.類似的異步調(diào)用的情況除了Ajax和setTimeout類方法之外,還包括指定DOM元素對(duì)于某事件的回調(diào)函數(shù)時(shí)倔撞,此時(shí)的this會(huì)自動(dòng)指向該DOM元素讲仰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市痪蝇,隨后出現(xiàn)的幾起案子鄙陡,更是在濱河造成了極大的恐慌,老刑警劉巖躏啰,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趁矾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡给僵,警方通過查閱死者的電腦和手機(jī)毫捣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)帝际,“玉大人蔓同,你說我怎么就攤上這事『荆” “怎么了牌柄?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)侧甫。 經(jīng)常有香客問我珊佣,道長(zhǎng),這世上最難降的妖魔是什么披粟? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任咒锻,我火速辦了婚禮,結(jié)果婚禮上守屉,老公的妹妹穿的比我還像新娘惑艇。我一直安慰自己,他們只是感情好拇泛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布滨巴。 她就那樣靜靜地躺著,像睡著了一般俺叭。 火紅的嫁衣襯著肌膚如雪恭取。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天熄守,我揣著相機(jī)與錄音蜈垮,去河邊找鬼。 笑死裕照,一個(gè)胖子當(dāng)著我的面吹牛攒发,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播晋南,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惠猿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了负间?” 一聲冷哼從身側(cè)響起紊扬,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唉擂,沒想到半個(gè)月后餐屎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玩祟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年腹缩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片空扎。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藏鹊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出转锈,到底是詐尸還是另有隱情盘寡,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布撮慨,位于F島的核電站竿痰,受9級(jí)特大地震影響脆粥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜影涉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一变隔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蟹倾,春花似錦匣缘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至豁陆,卻和暖如春柑爸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背献联。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工竖配, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人里逆。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓进胯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親原押。 傳聞我的和親對(duì)象是個(gè)殘疾皇子胁镐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 現(xiàn)在最熱門的前端框架,毫無(wú)疑問是 React 诸衔。上周盯漂,基于 React 的 React Native 發(fā)布,結(jié)果一...
    sakura_L閱讀 431評(píng)論 0 0
  • 1.JQuery 基礎(chǔ) 改變web開發(fā)人員創(chuàng)造搞交互性界面的方式笨农。設(shè)計(jì)者無(wú)需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性就缆。 1....
    LaBaby_閱讀 1,336評(píng)論 0 2
  • 你不知道JS:異步 第三章:Promises 在第二章,我們指出了采用回調(diào)來(lái)表達(dá)異步和管理并發(fā)時(shí)的兩種主要不足:缺...
    purple_force閱讀 2,068評(píng)論 0 4
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象谒亦,但只有一個(gè)實(shí)例竭宰,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式份招,...
    Obeing閱讀 2,073評(píng)論 1 10