其实每一个Javascript开发人员都不敢百分之百的保证自己对this这个东西有多么深入的了解,就好比泡面自己,用在其中,
却经常容易迷失在其中。
this并不指向自身
我们先来看一个简单的示例:
这个简单的示例我们可以总结出以下事情:
- this是在调用时所处的环境确定的;
- this的指向跟调用栈有很大关系,我们是在全局环境下通过最普通的方式调用了foo,而在调用栈中,全局Global环境永远是第一个被压入栈中,所以我们foo中的this指向了全局环境(Window)。我们通过下面的图即可看出来:
不过,我们需要注意一点,这段代码是运行在兼容模式下,如果你在代码中加入了严格模式,则this.bar会指向undefined
对象中的this
我们先来看一个函数在对象中做属性的示例:
在这个示例中,我们如约(符合我们逻辑的)看到了this指向了该对象,并从中打印出来了我们想要的结果。那是因为我们赋予了foo一个上下文,我们将foo的引用赋给了obj.foo,也就是obj包含了一个foo的引用关系,当我们调用obj.foo时,即是使用了obj的引用关系调用了foo,那这个关系既是其上下文,this即会绑定到了该上下文obj对象上。
但,程序是有魔力的(手动滑稽),有时候就是这么奇特:P,我们接下来看下面的例子:
这特么是什么鬼?外面套个setTimeout会影响this的指向吗?其实,这里就有一个细节需要我们仔细的思考了。在setTimeout中,我们是将obj.foo当做了setTimeout的第一个参数传递给了setTiemout方法,其内部实现的道理类似如下方式:
1 | // 伪代码 |
我们可以看到fn的调用关系,因此,我们知道了一件事:直接调用回调函数的话,this会丢失!当然这种看似不确定容易丢失的方式是可以稳定下来的。
使用call()和apply()以及bind()来绑定this
call和apply的绑定结果是没有区别的,唯一区别在他们的参数上。1
2
3
4
5
6
7
8
9
10
11
12function foo () {
console.log( this.bar )
}
var obj = {
bar: 10
}
var bar = 20
foo.call(obj) // 10
foo.apply(obj) // 10
所以,我们可以通过这种方式来修复 setTimeout的问题
1 | var baz = function () { |
我们在ES5中包含了一种绑定方式,即:
1 | Function.prototype.bind |
因此,上面的绑定方式我们也可以这样来固定住this
1 | var baz = foo.bind(obj) |
关于this,有太多细节可以讨论和记录,泡面暂时就先记录这么多吧,如果再有一些需要总结,就再开一篇~