JavaScript this 指向规则详解
�� 核心规则总结
JavaScript中this的指向遵循以下5个核心规则,按优先级从高到低排列:
- new绑定 - 最高优先级
- 显式绑定 - call/apply/bind
- 隐式绑定 - 对象方法调用
- 默认绑定 - 独立函数调用
- 箭头函数 - 特殊规则,不受上述规则影响
1. new绑定(New Binding)
规则说明
使用new关键字调用构造函数时,this指向新创建的实例对象。
案例演示
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function() {
console.log(`我是${this.name},今年${this.age}岁`);
};
}
const person1 = new Person('张三', 25);
const person2 = new Person('李四', 30);
person1.sayHello(); // "我是张三,今年25岁"
person2.sayHello(); // "我是李四,今年30岁"
console.log(person1.name); // "张三"
console.log(person2.name); // "李四"
特殊情况:构造函数返回对象
function Test(name) {
this.name = name;
this.method = function() {
console.log('普通方法:', this.name);
};
return {
name: "返回对象",
method: () => {
console.log('箭头方法:', this.name);
}
};
}
const t = new Test("构造参数");
console.log(t.name); // "返回对象"
t.method(); // "箭头方法: 构造参数"
解析:
- 构造函数返回对象,所以t指向返回的对象
- t.name是返回对象的name属性
- 箭头函数继承外层作用域的this,即构造函数内的this,所以是"构造参数"
2. 显式绑定(Explicit Binding)
规则说明
使用call、apply、bind方法显式指定this的指向。
案例演示
function greet() {
console.log(`你好,我是${this.name}`);
}
const person = { name: '王五' };
const student = { name: '赵六' };
// call方法
greet.call(person); // "你好,我是王五"
greet.call(student); // "你好,我是赵六"
// apply方法
greet.apply(person); // "你好,我是王五"
greet.apply(student); // "你好,我是赵六"
// bind方法
const boundGreet = greet.bind(person);
boundGreet(); // "你好,我是王五"
// 带参数的例子
function introduce(age, city) {
console.log(`我是${this.name},${age}岁,来自${city}`);
}
introduce.call(person, 28, '北京'); // "我是王五,28岁,来自北京"
introduce.apply(student, [22, '上海']); // "我是赵六,22岁,来自上海"
3. 隐式绑定(Implicit Binding)
规则说明
当函数作为对象的方法被调用时,this指向调用该方法的对象。
案例演示
const user = {
name: '小明',
age: 25,
profile: {
hobby: '编程',
showHobby: function() {
console.log(`${this.name}的爱好是${this.hobby}`);
}
},
sayHello: function() {
console.log(`你好,我是${this.name}`);
},
getAge: function() {
return this.age;
}
};
user.sayHello(); // "你好,我是小明"
console.log(user.getAge()); // 25
// 嵌套对象中的this
user.profile.showHobby(); // "undefined的爱好是编程" (this指向profile对象)
隐式绑定丢失的情况
函数的赋值只是把函数本事赋值给新变量,不会保留原来调用的上下文。this指向在调用的时候根据调用的方式确定
const obj = {
name: '对象',
method: function() {
console.log(this.name);
}
};
// 正常调用
obj.method(); // "对象"
// 方法赋值给变量,丢失隐式绑定
const fn = obj.method;
fn(); // undefined (默认绑定)
// 作为回调函数,丢失隐式绑定
setTimeout(obj.method, 1000); // undefined
// 解决方案1:使用箭头函数包装
setTimeout(() => obj.method(), 1000); // "对象"
// 解决方案2:使用bind
setTimeout(obj.method.bind(obj), 1000); // "对象"
4. 默认绑定(Default Binding)
规则说明
独立函数调用时,this指向全局对象(浏览器中是window,Node.js中是global)。
独立调用 = 函数调用时没有明确的调用者(没有.操作符)
案例演示
// 非严格模式
function globalTest() {
console.log(this); // window (浏览器) 或 global (Node.js)
console.log(this === window); // true (浏览器中)
}
globalTest(); // 独立函数调用
// 严格模式
'use strict';
function strictTest() {
console.log(this); // undefined
}
strictTest(); // undefined
// 混合模式
function mixedTest() {
console.log(this); // window
}
function strictWrapper() {
'use strict';
mixedTest(); // window (mixedTest不是严格模式)
}
5. 箭头函数(Arrow Functions)
规则说明
箭头函数没有自己的this,它会继承外层作用域的this。
案例演示
const obj = {
name: '对象',
// 普通方法
regularMethod: function() {
console.log('普通方法:', this.name); // "对象"
// 内部普通函数
function innerFunction() {
console.log('内部普通函数:', this.name); // undefined (默认绑定)
}
innerFunction();
// 内部箭头函数
const innerArrow = () => {
console.log('内部箭头函数:', this.name); // "对象" (继承外层this)
};
innerArrow();
},
// 箭头函数方法
arrowMethod: () => {
console.log('箭头函数方法:', this.name); // undefined (继承全局作用域)
}
};
obj.regularMethod();
obj.arrowMethod();
// 事件处理中的this
const button = document.createElement('button');
button.textContent = '点击我';
// 普通函数
button.addEventListener('click', function() {
console.log('普通函数:', this); // button元素
});
// 箭头函数
button.addEventListener('click', () => {
console.log('箭头函数:', this); // window (继承外层作用域)
});
�� 综合案例分析
案例1:多层嵌套调用
const obj = {
name: '外层对象',
inner: {
name: '内层对象',
method: function() {
console.log(this.name); // "内层对象"
function nested() {
console.log(this.name); // undefined (默认绑定)
}
nested();
}
}
};
obj.inner.method(); // 输出: "内层对象" 和 undefined
案例2:类中的this
class MyClass {
constructor(name) {
this.name = name;
}
// 普通方法
regularMethod() {
console.log('普通方法:', this.name);
}
// 箭头函数方法
arrowMethod = () => {
console.log('箭头方法:', this.name);
}
// 包含回调的方法
methodWithCallback() {
setTimeout(function() {
console.log('回调普通函数:', this.name); // undefined
}, 100);
setTimeout(() => {
console.log('回调箭头函数:', this.name); // 类实例的name
}, 200);
}
}
const instance = new MyClass('类实例');
instance.regularMethod(); // "普通方法: 类实例"
instance.arrowMethod(); // "箭头方法: 类实例"
instance.methodWithCallback(); // 输出: undefined 和 "回调箭头函数: 类实例"
�� 快速判断技巧
-
看调用方式:
obj.method()→ 隐式绑定new Function()→ new绑定func.call(obj)→ 显式绑定func()→ 默认绑定
-
看函数类型:
- 箭头函数 → 继承外层作用域
- 普通函数 → 按上述规则判断
-
看上下文:
- 严格模式 → 默认绑定为undefined
- 非严格模式 → 默认绑定为全局对象
-
特殊情况:
- 构造函数返回对象 → 覆盖this
- 方法赋值给变量 → 丢失隐式绑定
- 回调函数 → 通常丢失隐式绑定
📝 记忆口诀
"新显隐默箭" - 按优先级记忆:
- 新:new绑定
- 显:显式绑定
- 隐:隐式绑定
- 默:默认绑定
- 箭:箭头函数(特殊规则)
掌握了这些规则和案例,你就能准确判断任何JavaScript代码中this的指向了!