# 前言
本文非深入系列,而是自己总结得出,原因是深入系列对于 this 的描述过于复杂,引用了大量的 ES 标准进行论述,不适合面试的时候吹逼。
# 隐式绑定
关于 this,一般来说谁调用了方法,该方法的 this 就指向谁,例如:
function foo() {
console.log(this)
console.log(this.a)
}
var a = 3
var obj = {
a:2,
foo
}
foo() // 输出 window, 3
obj.foo() // 输出 obj, 2
foo()
输出 window,3 是因为是 window 调用了 foo 函数来;而 obj.foo()
输出为 obj,2 则是因为是 obj 调用了 foo,所以 this 就变为指向 obj。
如果存在多次调用,对象属性引用链只有一层或者说只有最后一层在调用位置中起作用,例如:
function foo() {
console.log(this.a)
}
var obj2 = {
a: 42,
foo,
}
var obj1 = {
a: 2,
obj2
}
obj1.obj2.foo() // 42,因为本质上是 obj2 调用了 foo
# 隐式丢失
一个常见的 this 绑定问题就是 隐式丢失 的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象中:
function foo() {
console.log(this.a)
}
var obj = {
a: 2,
foo
}
var a = 3
var bar = obj.foo
bar() // 3
虽然 bar 是 obj.foo
的一个引用,但实际上它引用的是函数的本身,因此此时 bar()
其实是一个不带任何修饰的函数调用,所以启用了默认绑定。更常见的是出现在传入回调函数中:
function foo() {
console.log(this.a)
}
function doFoo(fn) {
fn()
}
var obj = {
a: 2,
foo
}
var a = 3
doFoo(obj.foo)
# 显式绑定
简单来说就是:call,apply,bind 和 new
# 硬绑定
function foo(b){
console.log(this.a, b);
return this.a + b;
}
var obj = {
a: 2
};
var bar = function() {
return foo.apply(obj, arguments);
};
var b = bar(3);// 2 3
console.log(b);//5
在 bar 函数中,foo 使用了显式绑定,将 this 指向了 obj。
这里也可以使用 call、bind,同理。
# new 绑定
在传统面向对象的语言中,使用 new 初始化类的时候会调用类的构造函数,但是 JS 中的 new 的机制不完全一致。实际上在 JS 调用 new 的过程中,会自动执行下面的操作:
- 创建一个全新的对象
- 这个新对象的
__proto__
会和原对象的原型相连接 - 这个新对象被绑定到函数调用的 this
- 如果函数没有返回对象,则返回创建的对象
# this 的优先级
new绑定 > 硬绑定 > 隐式绑定 > 默认绑定
# 箭头函数
箭头函数不适用于这四种规则,而是根据外层(函数或全局)作用域来决定 this
function foo() {
return () => {
console.log(this.a)
}
}
var obj1 = {
a: 2
}
var obj2 = {
a: 3
}
var bar = foo.call(obj1)
bar.call(obj2) // 2
foo()
内部创建的箭头函数会捕获调用 foo()
的 this。由于 foo()
的 this 绑定到了 obj1,所以 bar 的 this 也会绑定到 obj1,箭头函数的绑定无法被修改。
← new模拟实现.md 作用域链.md →