JavaScript 有关this和bind的总结

在学习React利用ES6构建事件处理,需要利用Function.prototype.bind()来绑定this,很多人在这里会对this,bind有困扰。这里我把有关bind和this相关知识点做出总结,作出相关解答。

var that = this;

《JavaScript高级程序设计》有关this对象。this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象调用时,this等于那个对象。匿名函数的执行环境具有全局性,因此this对象通常指向window.
示例代码(取自《JavaScript高级程序设计》):

var name = 'the window';
var object = {
name:'my object',
getNameFunc:function(){
return function(){
return this.name;
}
}
};
console.log(object.getNameFunc()());//the window

那么,如果想要获取object中name的属性,则需要利用var that = this来保存当时的执行环境

var name = 'the window';
var object = {
name:'my object',
getNameFunc:function(){
var that = this;
return function(){
return that.name;
}
}
};
console.log(object.getNameFunc()());//'my object'

这里利用了闭包的特性,匿名函数中仍可以访问函数外部的变量。
利用var that = this确实能解决这样的执行环境上下文的问题。
再次举个例子:

var myObj = {
specialFunction:function(){},
anotherSpecialFunction:function(){},
getAsyncData:function(cb){
cb();
},
render:function(){
var that = this;
this.getAsyncData(function(){
that.specialFunction();
that.anotherSpecialFunction();
});
}
};

可以想象,如果把上述代码中的that该为this来使用,那么肯定报错。因为匿名函数的执行环境具有全局性,而在全局window对象中并没有specialFunction和anotherSpecialFunction方法。
报错信息如下:

Uncaught TypeError:Objcet [objcet global] has no method "specialFunction"

使用that是因为需要保持myObj引用对象的上下文

Function.prototype.bind

利用Function.prototype.bind同样可以保持myObj对象的上下文

render:function(){
this.getAsyncData(function(){
this.specialFunction();
this.anotherSpecialFunction();
}.bind(this));
}

来看看JavaScript中bind的实现原理:

Function.prototype.bind = function(scope){
var fn = this;
return function(){
return fn.apply(scope);
}
}

有关bind用法的简单示例:

var foo = {
x:3
}
var bar = function(){
console.log(this.x);
}
bar();
var boundFunc = bar.bind(foo);
boundFunc();

React中构建组件的时候,要保持组件的上下文,同样可以使用bind

class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

使用箭头函数可以保留父上下文

function Person() {
var that = this;
that.age = 0;
setInterval(function growUp() {
// The callback refers to the `that` variable of which
// the value is the expected object.
that.age++;
}, 1000);
}

使用箭头函数,不用把父上下文this赋值给其他变量

function Person(){
this.age = 0;
setInterval(() => {
this.age++; // |this| properly refers to the person object
}, 1000);
}
var p = new Person();