The best time for new beginnings is now
this的指向问题在javascript中一直比较困扰我。 一下总结了几种情况下this分别指向的内容。 总的来说就是根据执行环境或函数的执行方式不同,this的值有所不同。
这里假设执行环境是在浏览器端,所有的全局对象都是指window对象
定义在全局模式下
{
console.log(this === window); //true; 未定义任何函数的情况下,this 指向window
}
定义在函数内部
根据函数的调用方式不同,this的值可能不同
普通调用
{
a = function() {
return this;
}
b = function() {
"use strict"
return this;
}
a(); // 非严格模式: this指向全局对象
b(); // 严格模式: this = undefine;
}
对象内部调用
需要注意的是this的指向总是与他最近的执行环境相关。 看下面的列子:
{
o = {
a: function() {
return this;
},
b: {
c: function() {
return this;
}
}
};
o.a(); //作为对象的方法被调用, this指向调用该方法的对象本身,即o
o.b.c(); // c是作为o.b这个对象的方法被调用的,所以c返回的this是指向o.b这个对象的
//再看下面的例子
d = o.b.c;
d(); // d是在全局环境下调用的,所以返回的this是指向window对象的
}
但是如果是arrow function 定义的函数, 其this会被绑定到该函数外层执行环境的this上, 看下面的例子:
{
x = 10;
o2 = {
x: this.x, // 此时的this是指向window对象的
e: ( () => this),
f: function() {
var g = (() => {
console.log(this.e());
return this;
});
return g();
}
};
o2.e(); //window
o2.f(); //Object{}
}
执行o2.e()返回window对象。因为e的外层是o2, o2的this指向Global,所以e() 返回的this 是window对象。
o2.f 内部又定义了g 函数。该函数验证了两件事:
- g 本身的this指向;
- 不同环境下调用e(), 其this返回值会不会变化;
因为g的外层是f函数,f的this是指向o2对象本身的。所以g()返回的this是o2对象本身。也正是因为如此,如果 console.log(this.e())
才能够成功调用e(), 如果this不是执行o2, 那么 console.log(this.e())
就会报 Uncaught TypeError: this.e is not a function(…)
。 另外this.e()的返回结果仍然是window对象,这就说明了arrow function 的另一个特性,arrow function 的this在创建的时候就指定了(即其外层执行环境的this上)。当然如果可以指定其外层执行环境,那就另当别论了。
{
// 这里修改一下o2.f
o2.f = function() {
var g = (() => {
return this;
});
return g();
};
o3 = {};
o2.f.apply(o3);
}
这里通过apply 调用o2.f, 使of.f的this指向o3, 这样一来g()返回的this就指向了o3 本身。 去掉 console.log(this.e());
这行代码的原因就是为了避免报Uncaught TypeError: this.e is not a function(…)
错误(因为o3上没有定义e这个函数);
通过new调用
通过new调用的函数作为一个构造函数被执行去创建一个特定的实例对象。它主要做了3件事:
- 创建一个新对象,继承其构造函数的原型链;
- 执行该构造函数并把this绑定到新创建的对象上;
如果构造函数有返回对象则将刚返回对象作为new表达式的执行结果(),如果未返回对象,则将第一步创建的对象作为最终结果。
{
var Person = function(a, b) { this.name = a || ""; this.age = b || ""; }; var AnotherPerson = function(a,b) { this.name = a || ""; this.age = b || ""; return {name: "Simpson"}; }; p1 = new Person("Jane", 22); // {name: "Jane", age: "22"}; p2 = new AnotherPerson("Jane", 22); // {name: "Simpson"};
}
像上面的例子,this始终是指向第一步创建的对象,当构造函数没有返回对象时,p1得到的结果即是第一步创建的对象被赋值后的结果。 当构造函数具有返回对象时, this.name = a || ""; this.age = b || "";
其实也执行了,第一步创建的对象其实也被赋值了,只是最终结果被其返回值覆盖了。
通过call, apply调用
前面的例子已经提到过,通过apply可以指定function内部的this指向一个特定的对象,通过call方法也是一样的,只需将特定的对象作为参数传递进去,函数内部的this就会指定到这个特定的对象上。
{
var k = 1;
function j() {
return this.k;
}
var l = {
k: 10
};
j(); // 1; this指向window对象
j.apply(l); //10
j.call(l); //10
}
通过bind指定对象调用函数
bind和call,apply的作用类似,也是将function内部的this绑定到一个特定的对象上。
{
var k = 1;
function j() {
return this.k;
}
var l = {
k: 10
};
m = j.bind(l);
j(); // 1
m(); // 10
}