上周被一个超简单的问题困扰了很久:

当一个匿名函数作为callback被调用的时候,它的this context是什么???

答:难道不是使用这个callback的函数么??

问:如果这个函数是通过new关键字来实例化的呢?

答:呃…… 这个时候应该是这个实例本身?

答案是:

non-strict mode : window

strict mode: undefined

然而这个答案对于当时的我来说实在是太烧脑,
于是我花了两个工作天(所以其实并不是全天都在查资料啦)的时间来探究这个问题。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var Person = function (name) {
this.name = name;
console.log(this); /* => Person */
}
Person.prototype.say = function (word, cb) {
/* 这里的 this 是通过原型链继承下来的 */
console.log(this.name); /* this => Person */
if(cb && typeof cb === 'function'){
cb();
}
}
/* ======================== */
/* 直接 new 生成实例 */
/* ======================== */
var globalSam = new Person('global sam');
globalSam.say('hello', function() {
/* 为什么这里的 this 不是 Person ??? */
console.log(this); /* => non-strict : window | strict: undefined */
});
/* ======================== */
/* 在object中 new 生成实例 */
/* ======================== */
var simpleObject = {
keySam: new Person('key sam'),
log: function(){
this.keySam.say('hey', function(){
/* 为什么这里的 this 不是 simpleObject ??? */
console.log(this); /* => non-strict : window | strict: undefined */
});
}
}
simpleObject.log();

这让我想起了很久以前,自己看了一大堆资料以后,对this的一个总结:

當不清楚this的指向的時候,可以问自己一个问题:

「是谁(哪个object)调用了使用this的方法?」

「方法被调用時,this所依附的object是什么?」

再看我们的demo,是globalSam.saysimpleOject.log调用了作为callback的匿名函数。而globalSamsimpleObject其实是window.globalSamwindow.simpleObject,所以我们会log出那样的结果。

至此,也可以得出一个推论:任何匿名函数,只要在代码中没有明确地绑定context,他们的this都会指向全局(global context)或者浏览器的window对象。而当代码是在js的strict mode下执行的,它们的context就会是undefined

现在,如果我们这样改写上面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
var Person = function (name) {
this.name = name;
/* console.log(this) /* => Person */
}
Person.prototype.say = function (word, cb) {
/* 这里的 this 是通过原型链继承下来的 */
console.log(this.name); /* this => Person */
if(cb && typeof cb === 'function'){
console.log('====== logging cb ======');
cb();
console.log('====== logging do something ======');
this.doSomething = cb;
this.doSomething();
}
}
/* ======================== */
/* 直接 new 生成实例 */
/* ======================== */
var globalSam = new Person('global sam');
globalSam.say('hello', function() {
/* plain cb execution => non-strict : window | strict: undefined */
/* doSomething => Person */
console.log(this);
});
/* ======================== */
/* 在object中 new 生成实例 */
/* ======================== */
var simpleObject = {
keySam: new Person('key sam'),
logKeySam: function(){
this.keySam.say('hey', function(){
/* plain cb execution => non-strict : window | strict: undefined */
/* doSomething => Person */
console.log(this);
});
}
}
simpleObject.logKeySam();

参考文章:
[1] Understanding Scope and Context in JavaScript
[2] JAVASCRIPT: ANONYMOUS FUNCTIONS