再次学习Javascript的this

  其实每一个Javascript开发人员都不敢百分之百的保证自己对this这个东西有多么深入的了解,就好比泡面自己,用在其中,
却经常容易迷失在其中。

this并不指向自身

  我们先来看一个简单的示例:

about this

  这个简单的示例我们可以总结出以下事情:

  1. this是在调用时所处的环境确定的;
  2. this的指向跟调用栈有很大关系,我们是在全局环境下通过最普通的方式调用了foo,而在调用栈中,全局Global环境永远是第一个被压入栈中,所以我们foo中的this指向了全局环境(Window)。我们通过下面的图即可看出来:

GlobalScope在最底下

不过,我们需要注意一点,这段代码是运行在兼容模式下,如果你在代码中加入了严格模式,则this.bar会指向undefined

  

对象中的this

  我们先来看一个函数在对象中做属性的示例:

对象中的this

  在这个示例中,我们如约(符合我们逻辑的)看到了this指向了该对象,并从中打印出来了我们想要的结果。那是因为我们赋予了foo一个上下文,我们将foo的引用赋给了obj.foo,也就是obj包含了一个foo的引用关系,当我们调用obj.foo时,即是使用了obj的引用关系调用了foo,那这个关系既是其上下文,this即会绑定到了该上下文obj对象上。

  但,程序是有魔力的(手动滑稽),有时候就是这么奇特:P,我们接下来看下面的例子:

对象中的this

  这特么是什么鬼?外面套个setTimeout会影响this的指向吗?其实,这里就有一个细节需要我们仔细的思考了。在setTimeout中,我们是将obj.foo当做了setTimeout的第一个参数传递给了setTiemout方法,其内部实现的道理类似如下方式:

1
2
3
4
5
// 伪代码
function setTimeout( fn, delay) {
// -- delay some times
fn() // resolve fn
}

  我们可以看到fn的调用关系,因此,我们知道了一件事:直接调用回调函数的话,this会丢失!当然这种看似不确定容易丢失的方式是可以稳定下来的。

使用call()和apply()以及bind()来绑定this

  callapply的绑定结果是没有区别的,唯一区别在他们的参数上。

1
2
3
4
5
6
7
8
9
10
11
12
function foo () {
console.log( this.bar )
}

var obj = {
bar: 10
}

var bar = 20

foo.call(obj) // 10
foo.apply(obj) // 10

  所以,我们可以通过这种方式来修复 setTimeout的问题

1
2
3
4
5
var baz = function () {
foo.call(obj)
}

setTimeout(baz) // 10

  我们在ES5中包含了一种绑定方式,即:

1
Function.prototype.bind

  因此,上面的绑定方式我们也可以这样来固定住this

1
2
var baz = foo.bind(obj) 
baz() // 10

关于this,有太多细节可以讨论和记录,泡面暂时就先记录这么多吧,如果再有一些需要总结,就再开一篇~