this 指向绑定规则
1. 核心规则
在 JS 中,this 是在函数被调用时才确定的,而不是在定义时。判断 this 指向,只需看函数从哪里被调用。
(1) 默认绑定
独立函数调用。
- 非严格模式:
window (浏览器) 或 global (Node)。
- 严格模式:
undefined。
function foo() {
console.log(this.a);
}
var a = 2; // window.a
foo(); // 2 (非严格模式)
(2) 隐式绑定
函数作为对象的方法被调用。this 指向调用它的那个对象(只看最后一层)。
function foo() {
console.log(this.a);
}
var obj = { a: 2, foo: foo };
obj.foo(); // 2
// 链式调用
obj2.obj1.foo(); // obj1 (最后一层)
陷阱 (隐式丢失)
如果把对象方法赋值给变量,再调用变量,this 会丢失并退化为默认绑定。
var bar = obj.foo;
var a = "global";
bar(); // "global"
(3) 显式绑定
通过 call, apply, bind 强制指定 this。
var obj = { a: 2 };
foo.call(obj); // 2
硬绑定
bind 返回一个新函数,该函数的 this 被永久绑定,无法再被 call/apply 修改。
var bar = foo.bind(obj);
bar.call(window); // 2 (仍然是 obj)
(4) new 绑定
使用 new 关键字调用构造函数时,会创建一个新对象,并将 this 指向它。
(如果构造函数返回特定对象,则 this 失效,以返回对象为准)
function Foo(a) {
this.a = a;
}
var bar = new Foo(2);
console.log(bar.a); // 2
优先级:
new > 显式绑定 (bind) > 隐式绑定 > 默认绑定
2. 箭头函数 —— 规则之外
箭头函数根本没有自己的 this。它只会捕获其定义时外层作用域的 this 值。
一旦捕获,永久不变,甚至无法被 call/apply/bind/new 修改。
function foo() {
return (a) => {
// this 继承自 foo() 调用时的 this
console.log(this.a);
};
}
var obj1 = { a: 2 };
var obj2 = { a: 3 };
var bar = foo.call(obj1);
bar.call(obj2); // 2 (不会变为 3,因为箭头函数的 this 已经定死在 obj1 了)
3. 面试陷阱题
(1) 为什么 React 类组件方法需要 bind?
因为类方法默认没有绑定实例。当作为事件回调传递给 JSX 时 (onClick={this.handleClick}),实际执行时并不是 instance.handleClick() 而是直接调用引用,导致 this 丢失 (变为 undefined)。
(2) 手写 bind (核心考点)
需要处理 new 调用的优先级。如果被 bind 的函数后来被 new 调用了,那么 this 必须指向新实例,而不是 bind 绑定的对象。
Function.prototype.myBind = function (context, ...args) {
const fn = this;
return function F(...newArgs) {
if (this instanceof F) {
return new fn(...args, ...newArgs);
}
return fn.apply(context, [...args, ...newArgs]);
};
};
(3) setTimeout 中的 this
setTimeout 回调函数的执行环境是全局对象 (window),除非使用箭头函数或 bind。
var obj = {
fn: function () {
setTimeout(function () {
console.log(this);
}, 100);
},
};
obj.fn(); // window