别再猜 this:先看它属于谁,再看它指向谁
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
为什么要有
|
| 调用形式 | 绑定规则 | this 的指向 |
|---|---|---|
foo() | 默认绑定 | window |
obj.foo() | 隐式绑定 | obj |
oo.foo.foo() | 多层调用 | 离函数最近的调用对象 |
foo.call(obj) / foo.apply(obj) / foo.bind(obj)() | 显式绑定 | 指定的 obj |
new Foo() | new 绑定 | 新创建的实例 |
函数以 foo() 的形式独立执行,并且 this 没有被显式绑定或 new 绑定改变时,会使用默认绑定。此时函数中的 this 指向 window。
var a = 1;
function bar() {
var a = 2;
function foo() {
console.log(this.a);
}
foo();
}
bar(); // 1
这段代码可以分三步判断:
this 出现在 foo 中。foo() 是独立调用,使用默认绑定,所以 this 指向 window。var 声明的 a 会成为 window.a,因此输出 1。bar 内部的 a 是局部变量。函数嵌套关系不会改变 foo 的 this,所以这里不会输出 2。
当函数保存在对象属性中,并通过这个对象调用时,函数中的 this 指向该对象。
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo
};
obj.foo(); // 1
对象属性 foo 保存的是函数引用。真正决定 this 的是调用表达式 obj.foo():调用点前有 obj,所以 foo 中的 this 指向 obj,最终读取到 obj.a。
如果同一个函数由另一个对象调用,this 也会随之改变:
var another = {
a: 2,
foo: foo
};
another.foo(); // 2
这正是 this 便于复用的原因:函数本身没有改变,调用者决定了它操作哪个对象。
多层对象调用不会把最外层对象一直传递给函数。判断时只看最终调用点,this 指向离函数最近的调用对象。
function foo() {
console.log(this.a);
}
var obj = {
a: 1,
foo: foo
};
var oo = {
a: 2,
foo: obj
};
oo.foo.foo(); // 1
oo.foo 的值是 obj,所以 oo.foo.foo() 可以看成由 obj 调用 foo。直接调用者是 obj,而不是最外层的 oo,因此 this.a 的结果是 1。
不想由调用形式自动判断时,可以使用 call、apply 或 bind 直接指定函数中的 this。
function foo(x, y) {
console.log(this.a, x + y);
}
var GGbond = { a: 1 };
var FF = { a: 2 };
var CRQ = { a: 3 };
foo.call(GGbond, 1, 2); // 1 3
foo.apply(FF, [2, 3]); // 2 5
const bar = foo.bind(CRQ, 3, 4);
bar(); // 3 7
三种方法都能指定 this,区别在于调用时机和参数形式:
call 立即调用函数,从第二个参数开始逐个传递实参。apply 立即调用函数,使用数组传递实参。bind 不会立即调用,而是返回一个绑定好 this 的新函数,需要再次调用。new 绑定函数通过 new 调用时,其中的 this 指向新创建的实例对象。构造函数中通过 this 添加的属性,也会出现在这个实例上。
function Person() {
this.name = "GGBond";
}
const p = new Person();
console.log(p.name); // GGBond
调用 new Person() 时,Person 中的 this 指向实例 p,所以 this.name = "GGBond" 等同于给 p 添加 name 属性。
箭头函数没有自己的 this,也不使用普通函数的绑定规则。它会沿用定义位置外层作用域中的 this;当箭头函数写在普通函数内部时,它会沿用该普通函数的 this。
function foo() {
var fn = () => {
this.a = 2;
};
fn();
}
var obj = {
a: 1,
bar: foo
};
obj.bar();
console.log(obj.a); // 2
这段代码的执行过程是:
obj.bar() 调用普通函数 foo,所以 foo 的 this 指向 obj。fn 没有自己的 this,于是沿用 foo 的 this。this.a = 2 实际修改的是 obj.a,最终输出 2。箭头函数不能作为构造函数,因此不能通过 new 调用。
普通函数的 this 取决于调用方式;箭头函数的 this 取决于定义位置的外层作用域。抓住这一区别,就能快速判断代码中的 this 指向。
阅读原文:点击这里