文章目录
- [一、this 是什么?](#一、this 是什么?)
- [二、四条铁律:this 绑定规则](#二、四条铁律:this 绑定规则)
-
- [1. 默认绑定 ------ 独立函数调用](#1. 默认绑定 —— 独立函数调用)
- [2. 隐式绑定 ------ 作为对象方法调用](#2. 隐式绑定 —— 作为对象方法调用)
- [3. 显式绑定 ------ call、apply、bind](#3. 显式绑定 —— call、apply、bind)
- [4. new 绑定 ------ 构造函数调用](#4. new 绑定 —— 构造函数调用)
- [三、箭头函数的 this](#三、箭头函数的 this)
- [四、this 绑定优先级](#四、this 绑定优先级)
- 五、常见场景实战
-
- [1. 事件处理函数中的 this](#1. 事件处理函数中的 this)
- [2. 类(Class)中的 this](#2. 类(Class)中的 this)
- [3. 回调函数中的 this](#3. 回调函数中的 this)
- 六、经典面试题分析
- 七、最佳实践总结
一、this 是什么?
简单说,this 是函数执行时的上下文对象。在绝大多数情况下,函数的调用方式决定了 this 的值。它不能在函数定义时确定,只有在函数被调用时才会绑定。
理解这一点至关重要:同一个函数,不同方式调用,this 截然不同。
二、四条铁律:this 绑定规则
1. 默认绑定 ------ 独立函数调用
当函数直接调用(非严格模式)时,this 指向全局对象(浏览器中是 window,Node.js 中是 global)。
bash
function showThis() {
console.log(this);
}
showThis(); // window (或 global)
如果是严格模式('use strict'),this 为 undefined。
bash
function strictShow() {
'use strict';
console.log(this);
}
strictShow(); // undefined
2. 隐式绑定 ------ 作为对象方法调用
函数作为对象的属性被调用时,this 指向调用该方法的对象(即点号前面的对象)。
bash
const obj = {
name: 'Alice',
sayName() {
console.log(this.name);
}
};
obj.sayName(); // Alice,this 指向 obj
常见陷阱:隐式丢失
将方法赋值给变量或作为回调传递时,会丢失原对象,回退为默认绑定。
bash
const fn = obj.sayName;
fn(); // undefined (非严格模式下 window.name 通常为 '' 或 undefined)
在 setTimeout 中也一样:
bash
setTimeout(obj.sayName, 100); // this 指向 window
3. 显式绑定 ------ call、apply、bind
这三大方法可以强制指定 this。
call(thisArg, arg1, arg2, ...):立即执行函数,参数逐个传递。
apply(thisArg, args):立即执行函数,参数以数组形式传递。
bind(thisArg, ...):返回一个绑定了 this 的新函数,不立即执行。
bash
function greet(greeting) {
console.log(`${greeting}, ${this.name}`);
}
const user = { name: 'Bob' };
greet.call(user, 'Hi'); // Hi, Bob
greet.apply(user, ['Hello']); // Hello, Bob
const boundGreet = greet.bind(user, 'Hey');
boundGreet(); // Hey, Bob
注意:一旦使用 bind 绑定,后续再使用 call/apply 也无法改变 this(箭头函数同样无法改变,见下文)。
4. new 绑定 ------ 构造函数调用
使用 new 调用函数时,会创建一个全新的对象,并将 this 绑定到这个新对象上。
bash
function Person(name) {
this.name = name;
}
const p = new Person('Carol');
console.log(p.name); // Carol
new 做了四件事:
1、创建一个空对象。
2、该对象的 proto 指向构造函数的 prototype。
3、将 this 绑定到该对象上,执行构造函数。
4、如果构造函数没有返回对象,则返回 this。
三、箭头函数的 this
箭头函数没有自己的 this。它会捕获定义时外层作用域的 this 作为自己的 this,且之后无法被改变(call、apply、bind 均无效)。
bash
const obj = {
name: 'Dave',
regularFunc() {
setTimeout(function() {
console.log('regular:', this.name); // undefined (或 window.name)
}, 100);
},
arrowFunc() {
setTimeout(() => {
console.log('arrow:', this.name); // Dave,捕获到 arrowFunc 的 this 即 obj
}, 100);
}
};
obj.regularFunc();
obj.arrowFunc();
这解决了回调函数中 this 丢失的经典难题。但也要注意:箭头函数不能用作构造函数(new 会报错)。
四、this 绑定优先级
当多个规则同时出现时,优先级为:
new 绑定 > 显式绑定(call/apply/bind) > 隐式绑定 > 默认绑定
验证一下:
bash
function foo() { console.log(this.a); }
const obj1 = { a: 1, foo };
const obj2 = { a: 2 };
obj1.foo.call(obj2); // 2 (显式 vs 隐式 → 显式赢)
const bound = foo.bind(obj1);
new bound(); // undefined,new 会覆盖 bind,this 指向新对象
五、常见场景实战
1. 事件处理函数中的 this
DOM 事件中,this 默认指向绑定事件的元素(除 IE attachEvent 外)。
bash
button.addEventListener('click', function() {
console.log(this); // 指向 button 元素
});
若在回调中使用箭头函数:
bash
button.addEventListener('click', () => {
console.log(this); // 捕获外层 this,可能是 window
});
2. 类(Class)中的 this
ES6 类的方法默认启用严格模式,且 this 容易在赋值传递时丢失。
bash
class Counter {
constructor() {
this.count = 0;
}
increment() {
this.count++;
}
}
const c = new Counter();
const inc = c.increment;
inc(); // TypeError: Cannot read property 'count' of undefined
解决方案:
在构造函数中使用 bind:this.increment = this.increment.bind(this);
使用类字段(箭头函数语法):increment = () => { this.count++; }
3. 回调函数中的 this
在 map、filter、forEach 等数组方法中可传入 thisArg 作为第二个参数,或直接使用箭头函数。
bash
const obj = { multiplier: 2 };
[1, 2, 3].map(function(x) {
return x * this.multiplier;
}, obj); // [2, 4, 6]
// 或者
[1, 2, 3].map(x => x * obj.multiplier);
六、经典面试题分析
bash
var length = 10;
function fn() { console.log(this.length); }
var obj = {
length: 5,
method: function(fn) {
fn(); // ①
arguments[0](); // ②
}
};
obj.method(fn, 1);
答案:① 10(或 undefined严格模式),② 2。
解析:
①:fn() 独立调用,this 指向 window,window.length = 10(或严格模式 undefined)。
②:arguments0 相当于 arguments.0(),arguments 对象调用了 fn,隐式绑定 this 为 arguments,而 arguments.length 是实参个数(2 个:fn 和 1),所以输出 2。
这道题极好地体现了隐式绑定与独立调用的差异。
七、最佳实践总结
1、优先使用箭头函数 来处理回调,避免 this 丢失问题,但需清楚它没有自己的 this。
2、在类中,若方法需作为回调传递,在构造函数内 bind 或使用箭头函数类字段。
3、普通函数用 call/apply/bind 显式绑定,尤其在工具函数中。
4、避免混用 var 和 this 在对象方法内,建议统一用 this 访问实例属性。
5、开启严格模式 是个好习惯,能让默认绑定的 undefined 尽早暴露错误。
6、理解优先级,遇到复杂 this 问题时按 new → 显式 → 隐式 → 默认 的顺序推理。