/
Update
6 min read
中文 this指向
一、核心规则:this 指向谁?#
this 的指向取决于调用方式,而非定义位置:
┌─────────────────┬─────────────────────────────┐
│ 调用方式 │ this 指向 │
├─────────────────┼─────────────────────────────┤
│ 普通函数调用 │ window(严格模式 undefined) │
│ 对象方法调用 │ 调用该方法的对象 │
│ new 构造函数 │ 新创建的实例对象 │
│ call/apply/bind │ 指定的第一个参数 │
│ 箭头函数 │ 外层作用域的 this(定义时确定)│
│ DOM 事件回调 │ 触发事件的 DOM 元素 │
└─────────────────┴─────────────────────────────┘plaintext二、七种场景详解#
1. 全局上下文#
console.log(this); // window(浏览器)或 global(Node.js)javascript2. 普通函数调用(默认绑定)#
function foo() {
console.log(this);
}
foo(); // window(非严格模式)javascript严格模式下:
'use strict';
function foo() {
console.log(this); // undefined
}
foo();javascript3. 对象方法调用(隐式绑定)#
const obj = {
name: 'Alice',
sayHi() {
console.log(this.name); // this → obj
}
};
obj.sayHi(); // "Alice"javascript陷阱:赋值后调用会丢失绑定
const fn = obj.sayHi;
fn(); // undefined(this 指向 window,因为 fn() 是独立调用)javascript4. 构造函数调用(new 绑定)#
function Person(name) {
this.name = name; // this → 新创建的实例
}
const p = new Person('Bob');
console.log(p.name); // "Bob"javascriptnew 做了什么:
- 创建一个空对象
- 将
this指向这个空对象 - 执行构造函数代码
- 返回这个对象
5. 显式绑定:call / apply / bind#
function greet() {
console.log(`Hello, ${this.name}`);
}
const user = { name: 'Charlie' };
greet.call(user); // "Hello, Charlie" —— 立即调用,参数逐个传
greet.apply(user); // "Hello, Charlie" —— 立即调用,参数数组传
const boundGreet = greet.bind(user); // 返回新函数,永久绑定 this
boundGreet(); // "Hello, Charlie"javascript三者区别:
| 方法 | 调用时机 | 传参方式 |
|---|---|---|
call | 立即执行 | fn.call(thisArg, arg1, arg2) |
apply | 立即执行 | fn.apply(thisArg, [arg1, arg2]) |
bind | 返回新函数,稍后执行 | fn.bind(thisArg, arg1, arg2) |
6. 箭头函数(词法绑定)#
箭头函数没有自己的 this,它继承外层作用域的 this。
const obj = {
name: 'David',
regularFn: function() {
console.log(this.name); // "David" —— obj 调用
setTimeout(function() {
console.log(this.name); // undefined —— 独立调用,this → window
}, 100);
setTimeout(() => {
console.log(this.name); // "David" —— 继承外层 regularFn 的 this
}, 100);
}
};
obj.regularFn();javascript经典面试题:
const obj = {
name: 'Eve',
sayHi: () => {
console.log(this.name); // undefined!
}
};
obj.sayHi();
// 箭头函数在全局作用域定义,this 指向 window
// obj.sayHi() 的调用方式不影响箭头函数的 thisjavascript正确写法:
const obj = {
name: 'Eve',
sayHi() { // 简写方法(非箭头函数)
console.log(this.name); // "Eve" ✅
}
};javascript7. DOM 事件回调#
const button = document.getElementById('btn');
button.addEventListener('click', function() {
console.log(this); // button 元素
});
button.addEventListener('click', () => {
console.log(this); // window(继承外层作用域)
});javascript三、优先级规则#
当多种规则冲突时,优先级从高到低:
new 绑定 > 显式绑定(call/apply/bind)> 隐式绑定(对象方法)> 默认绑定plaintextfunction foo() {
console.log(this.a);
}
const obj1 = { a: 1, foo };
const obj2 = { a: 2, foo };
obj1.foo(); // 1(隐式绑定)
obj2.foo.call(obj1); // 1(显式绑定 > 隐式绑定)
new obj1.foo(); // undefined(new 绑定 > 隐式绑定)javascript四、常见陷阱与解决方案#
陷阱 1:回调函数丢失 this#
const obj = {
name: 'Frank',
greet() {
console.log(`Hi, ${this.name}`);
}
};
setTimeout(obj.greet, 100); // "Hi, undefined" —— this 丢失javascript解决方案:
// 方案 1:箭头函数包装
setTimeout(() => obj.greet(), 100);
// 方案 2:bind
setTimeout(obj.greet.bind(obj), 100);
// 方案 3:类字段 + 箭头函数(React 常用)
class MyClass {
greet = () => {
console.log(this.name); // 永远指向实例
}
}javascript陷阱 2:React 类组件中的 this#
class Button extends React.Component {
handleClick() {
console.log(this); // undefined!(严格模式下)
}
render() {
return <button onClick={this.handleClick}>Click</button>;
// 传递的是函数引用,调用时 this 丢失
}
}jsx解决方案:
class Button extends React.Component {
// 方案 1:构造函数中 bind
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
// 方案 2:类字段箭头函数(最简洁)
handleClick = () => {
console.log(this); // 正确指向组件实例
}
render() {
return <button onClick={this.handleClick}>Click</button>;
}
}jsx五、快速判断口诀#
1. 箭头函数?→ 找外层作用域的 this
2. new 调用?→ 指向新实例
3. call/apply/bind?→ 指向指定对象
4. 对象.方法()?→ 指向该对象
5. 直接调用函数?→ window(严格模式 undefined)plaintext六、现代开发中的最佳实践#
| 场景 | 推荐做法 |
|---|---|
| React 类组件 | 使用类字段箭头函数定义方法 |
| 需要保留 this 的回调 | 箭头函数或 .bind(this) |
| 不需要动态 this 的工具函数 | 箭头函数(避免 this 干扰) |
| 明确指定 this | 使用 call/apply/bind |
理解 this 的关键在于:不要看函数定义在哪里,要看函数是怎么被调用的。箭头函数是唯一的例外——它的 this 在定义时就确定了。