this
1. 默认绑定 (Default Binding)
这是最基础的规则。当函数独立调用,没有带任何修饰符(没有 new,没有 obj.,没有 call)时,应用此规则。
- 规则:
- 非严格模式:
this指向全局对象(浏览器中是window,Node.js 中是global)。 - 严格模式 (
'use strict'):this指向undefined。
- 非严格模式:
js
12345
function sayHi() {
console.log(this);
}
sayHi(); // window (非严格模式) / undefined (严格模式)
面试陷阱:这也是你之前做错的那道题的原因。
const say = person.sayName; say();这里say是独立调用的,所以this丢了,变成了全局或 undefined。
2. 隐式绑定 (Implicit Binding)
当函数作为对象的方法被调用时,this 指向那个对象。
- 规则:
this指向调用链中最后一层的对象。
JavaScript
12345678
const user = {
name: 'Alice',
greet: function() {
console.log(this.name);
}
};
user.greet(); // Alice -> user 调用了 greet
隐式丢失(常见坑):
如果把对象的方法赋值给一个变量,或者作为参数传入回调函数,隐式绑定会丢失,退化为默认绑定。
JavaScript
1234
const fn = user.greet;
fn(); // undefined (因为变成了独立调用)
setTimeout(user.greet, 100); // undefined (回调函数通常也是独立调用的)
3. 显式绑定 (Explicit Binding): call, apply, bind
这三个方法都在 Function.prototype 上,允许你强制指定函数的 this 指向。
A. call 和 apply
这两个方法非常相似,都会立即执行函数。区别仅在于传递参数的方式。
call(thisArg, arg1, arg2, ...):参数是一个接一个传的。apply(thisArg, [argsArray]):参数是放在一个数组里传的。
JavaScript
1234567891011
function introduce(lang, hobby) {
console.log(`${this.name} likes ${lang} and ${hobby}`);
}
const p1 = { name: 'Bob' };
// 使用 call:参数逗号隔开
introduce.call(p1, 'JS', 'Gaming'); // "Bob likes JS and Gaming"
// 使用 apply:参数放数组里
introduce.apply(p1, ['JS', 'Gaming']); // "Bob likes JS and Gaming"
记忆口诀:
apply 对应array(数组),call 对应comma(逗号)。
B. bind
bind 不会立即执行函数,而是返回一个新的函数。这个新函数的 this 被永久地“锁死”(硬绑定)到了传入的第一个参数上。
- 场景:常用于 React 类组件的事件处理,或者解决
setTimeout中的this丢失问题。
JavaScript
1234567891011
const p2 = { name: 'Charlie' };
function sayName() {
console.log(this.name);
}
const boundFn = sayName.bind(p2); // 只是绑定,没执行
boundFn(); // "Charlie"
// 即使之后再 call 别人,也改变不了 bind 的结果
boundFn.call({ name: 'Dave' }); // 依然是 "Charlie"
4. new 绑定 (New Binding)
当使用 new 关键字调用构造函数时,会发生以下步骤:
- 创建一个全新的空对象。
- 将这个对象的
[[Prototype]]链接到构造函数的prototype。 - 将函数内部的
this指向这个新对象。 - 如果函数没有返回其他对象,则自动返回这个新对象。
JavaScript
123456
function Person(name) {
this.name = name; // this 指向新创建的实例
}
const tom = new Person('Tom');
console.log(tom.name); // "Tom"
5. 特殊规则:箭头函数 (Arrow Function)
箭头函数没有自己的 this。它不遵循以上 4 条规则。
- 规则:箭头函数的
this完全取决于定义时(也就是写代码时)所处的外层(词法)作用域。 - 特点:
- 一旦确定,无法被
call、apply、bind修改。 - 常用于解决回调函数中
this指向错误的问题。
- 一旦确定,无法被
JavaScript
123456789101112131415161718
const obj = {
name: 'Arrow',
// 定义时,外层作用域是全局(对象字面量不构成作用域)
say: () => {
console.log(this.name);
},
// 普通函数创造了一个作用域
waitAndSay: function() {
// 这里的箭头函数定义在 waitAndSay 内部
// 所以它继承 waitAndSay 的 this (即 obj)
setTimeout(() => {
console.log(this.name);
}, 100);
}
};
obj.say(); // undefined (指向全局)
obj.waitAndSay(); // "Arrow" (正确继承了 obj)
总结与优先级 (Priority)
如果同一个调用位置存在多种规则,谁说了算?优先级如下(从高到低):
new绑定 (最高)- 显式绑定 (
call,apply,bind) - 隐式绑定 (
obj.func()) - 默认绑定 (
func()) (最低)
箭头函数是一个特例,它忽略所有绑定规则(除了 new,但箭头函数不能被 new),只认词法作用域。
一张表复习 call, apply, bind
| 特性 | call | apply | bind |
|---|---|---|---|
| 立即执行? | 是 | 是 | 否 (返回新函数) |
| 参数形式 | arg1, arg2, ... | [arg1, arg2, ...] | arg1, arg2, ... |
| 用途 | 借用方法、改变上下文 | 处理数组参数 (如 Math.max) | 预设参数、回调函数绑定 |