前言
以下内容只针对非严格模式, 严格模式区不说了
如果看过 《JavaScript 高级程序设计(第 3 版)》 的人都应该有印象,里面关于 this 对象是这么形容的
this 对象是在运行时基于函数的执行环境绑定的:在全局函数中,this 等于 window,而当函数被作为某个对象的方法调用时,this 等于那个对象。不过,匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window。
demo1
示例如下
var a = 10,
obj = {a: 20};
function log() {
var a = 30;
console.log(this.a); // 依然指向全局
};
console.log(this.a); // 全局
log(); // 方法
log.call(obj); // 指向 obj
;
(function () {console.log(this.a) // 匿名函数
})()
// 10
// 10
// 20
// 10
目前为止,一切都符合预期表现。然后我们再看些复杂点的写法。
demo2
var a = 10,
obj = {
a: 20,
log: function () {console.log(this.a); // 指向 obj
}
};
function log() {function _log() {console.log(this.a); // 指向全局
};
_log();};
obj.log(); // 调用对象方法
log(); // 调用方法内部方法
// 20
// 10
虽然有点绕,但是还是可以明白的,this 指向调用方法该对象,匿名函数 this 指向全局 。
看到这里我们好像已经大概明白 this 是怎么一回事了,然后我们再写复杂点
demo3
var a = 10,
obj = {
a: 20,
b: this.a + 30
};
console.log(obj.b); // 40
是不是又猛的怀疑人生了?明明应该 this 是指向 obj 得出 50,为什么会指向全局得出 40 的!?
想想上面说的当函数被作为某个对象的方法调用时,this 等于那个对象。难道属性值又是另一回事?
带着疑问我们直接打印出 this 看看
var a = 10,
obj = {
a: 20,
b: this
};
console.log(obj.b === window); //true
所以属性值里的 this 真的指向全局,于是我们可以假设得出一个结论:当函数被作为某个对象的方法调用时,this 等于那个对象,而某个对象的属性值的 this 是直接指向全局的
听起来好拗口,是不是意思对象的方法 this 指向对象,属性值指向全局!?
demo4
带着疑问我们直接看例子
var a = 10,
obj = {
a: 20,
log: function () {console.log(this.a);
}
};
var fn = obj.log;
fn(); // 调用赋值方法 10
正如前面所说,当函数被作为某个对象的方法调用时,this 等于那个对象。如果不是该对象直接调用时,this 指向调用对象。
基于这个结论, 即使中间经过几层函数调用也一样的
var a = 10,
obj = {
a: 20,
log: function () {console.log(this.a); // 指向 obj
}
};
function all_log(fn) {fn();
}
all_log(obj.log); // 10
看到了吧, 即使经过一个中间函数调用, 实际相当于 window 调用对象方法, 所以 this 也是指向全局的.
demo5
然后看看经常会用到的函数 return 出的 this 指向
var a = 10,
obj = {
a: 20,
log: function () {return function () {console.log(this.a); // 指向??
}
}
};
obj.log()(); //10
衹要基础稍好也能理解,因为 obj.log()返回一个匿名函数,然后相当于在全局下再调用匿名函数,因此 this 直接指向全局了。书里官方点的解释就是
每个函数在被调用时都会自动取得两个特殊变量:
this
和arguments
。内部函数在搜索这两个变量时,只会搜索到其活动对象为止,因此永远不可能直接访问外部函数中的这两个变量(例子是指 obj)
需要注意的是 如果没有明确指向全局的话 this 实际上都是指向 undefined 的,衹是非严格模式下,它会被自动指向全局对象。
function log() {
'use strict';
console.log(this === window);
}
log(); // 独立调用 false
window.log(); // window 下调用 true
demo6
书里还提到一个挺有意思的写法
var a = 10,
obj = {
a: 20,
log: function () {console.log(this.a); // 指向??
}
};
(obj.log = obj.log)(); // 先赋值再调用 10
你会发现居然 this 也指向全局了。
因为代码先执行了一条赋值语句,然后再调用赋值后的结果。因为这个赋值表达式的值是函数本身,所以 this 的值不能得到维持,结果就返回了 10。
demo7
如果是作为 构造函数
使用的情况下
function Person() {
this.name = 'mike';
this.log = function () {console.log(this);
}
};
var man = new Person();
man.log();//Person {name: "mike", log: function}
里面的 this 是指向 new 出来的对象, 详情可以看看我之前写的博文关于 Javascript 中的 new 运算符, 构造函数与原型链一些理解
demo8
还有种情况是通过 call
或apply
修改 this 指向, 它会强制指向方法的第一个参数
var a = 10,
obj = {a: 20};
function log() {console.log(this.a);
};
log.call(obj); // 指向 obj
log.apply(obj); // 指向 obj
基于上面的情况, 我们可以得出几个结论
1, this 对象是在运行时基于函数的执行环境绑定的
2, 如果没有明确指向全局的话 this 实际上都是指向 undefined 的,衹是非严格模式下,它会被自动指向全局对象
3, 当函数被作为某个对象的方法调用时,this 等于那个对象
4, 当某个对象的属性值里的 this 是直接指向全局对象
5, 匿名函数的执行环境具有全局性,因此其 this 对象通常指向全局对象
6, 构造函数中的 this 会指向它实例化对象
7, call 或 apply 方法能强制修改 this 指向方法指定对象