@frank-shaw
2017-02-08T10:38:03.000000Z
字数 2291
阅读 2039
javaScript
原文链接:https://www.sitepoint.com/bind-javascripts-this-keyword-react/
JavaScript的this关键字会令很多开发者感到很困惑。与其他有明确类模型(即使ES6开始有了class关键字,但是底层实现上依然是原型链模型)的语言不同,JavaScript中的this表示的含义通常是不明确的,当你在处理回调函数的时候,此问题更为突出。
因为React在类中使用this关键字来指代component的上下文环境,因此在使用React的时候也存在同样的困惑。在使用React的component时,你可能会看到如下代码:
this.setState({ loading: true });
fetch('/').then(function loaded() {
this.setState({ loading: false });
});
上面这段代码会报错,因为“this.setState is not a function”。这是因为promise的回调函数被调用的时候,函数的内部上下文环境发生了变化,this指向了错误的对象。让我们来看看可以如何来解决这个问题。
这种方法在component的作用域顶层,使用了另一个变量来指向this变量:
var component = this;
component.setState({ loading: true });
fetch('/').then(function loaded() {
component.setState({ loading: false });
});
这种方法对于初学者来讲,是较好理解的(即使你有可能不明白为什么这样做可行,但它确实有效)。它给了一个视觉上的保证:你所指向的上下文环境是正确的。
这种方法需要我们在运行回调函数时候为其注入正确的上下文环境。
this.setState({ loading: true });
fetch('/').then(function loaded() {
this.setState({ loading: false });
}.bind(this));
javascript中的所有函数都包含有bind方法,它允许你指定函数内部的this的指代的具体对象。当一个函数在运行过程中被绑定上下文环境之后,那么它的上下文环境就不允许改变了。
实际上,bind函数仅仅是创建了一个新的函数(该函数指定好了上下文)。源码的简化版(实际上会更加复杂)如下:
Function.prototype.bind = function (scope) {
var fn = this;
return function () {
return fn.apply(scope);
};
}
React允许开发者在component类中定义任意方法(通过React.createClass方法),同时这些方法都会自动添加上合适的上下文。当然,仅仅限制在React.createClass方法内部。
React.createClass({
componentWillMount: function() {
this.setState({ loading: true });
fetch('/').then(this.loaded);
},
loaded: function loaded() {
this.setState({ loading: false });
}
});
在使用React的时候,这当然为开发者省下了不少的精力。它允许你直接使用已经命名的函数。事实上,如果你尝试在component方法中添加.bind(this)方法,React会发出警告说:
bind(): You are binding a component method to the component. React does this for you automatically in a high-performance way, so you can safely remove this call.
不过,需要记住的是:这个自动绑定的功能在ES6中并没有实现(在ES7中可使用)。
箭头函数的引入有两个方面的影响:一是更简短的函数书写,二是对 this 的词法解析(箭头函数会捕获其所在上下文的this值,作为自己的this值)。
this.setState({ loading: true });
fetch('/').then(() => {
this.setState({ loading: false });
});
上面的代码运行起来完全没问题。
当然,箭头函数的一个弊端就是我们无法对我们的函数进行命名了。这会对调试造成一定的困扰。
ES7中有了新的提案:针对bind操作,引入了::作为操作符,具体操作方式为: ::左侧为被操作的数值,右侧为操作的函数。我们对应的表达式可以写成:
this.setState({ loading: true });
fetch('/').then(this::() => {
this.setState({ loading: false });
});
有一些函数允许将this作为一个特定的变量,放入到表达式中。其中的一个例子就是map函数,允许this值作为它的最后一个参数:
items.map(function(x) {
return <a onClick={this.clicked}>x</a>;
}, this);