異步編程中的.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元素讲仰。