简单总结:只要函数不是被“对象.函数()”直接调用,而是被赋值后调用,大概率会发生隐式丢失哦~
规则4:显式绑定 → 主动绑定this,精准控制指向
当我们想主动控制this的指向时,就可以用显式绑定——通过call、apply、bind三个方法,手动将this绑定到指定对象上。这三个方法的用法有细微区别,结合实例一次性搞懂:
// 共同前提
var obj = { a: 1 }
function foo(x, y) {
console.log(this.a, x + y); // this指向我们手动绑定的obj
}
// 1. call方法:直接调用函数,参数逐个传递
foo.call(obj, 1, 2) // 输出:1 3 ✅(call绑定this为obj,参数1、2逐个传入)
// 2. apply方法:直接调用函数,参数以数组形式传递
var arr = [1, 2]
foo.apply(obj, arr) // 输出:1 3 ✅(apply参数是数组,会自动解构)
// 3. bind方法:不直接调用函数,返回一个“绑定了this”的新函数
const bar = foo.bind(obj, 2, 4) // 绑定this为obj,预设参数2、4
bar(3) // 调用新函数,额外传入参数3,最终x=2,y=4+3=7?不!注意:bind预设参数在前
// 正确解析:bind预设的2、4是前两个参数,bar(3)是第三个参数?不,foo只有x、y两个参数
// 最终x=2,y=4,输出:1 6 ✅(bind绑定后,参数会固定,后续传入的参数会忽略多余的)
// 实例1:正常new调用,this指向实例
function Animal() {
this.name = '米奇' // this指向new创建的实例p
}
let a = new Animal()
let a2 = new Animal()
console.log(a.name); // 输出:米奇 ✅(a是实例,this指向a)
console.log(a2.name); // 输出:米奇 ✅(a2是另一个实例,this指向a2)
// 实例2:特殊情况——构造函数return引用类型,new绑定失效(如果renturn 原始对象,对new的绑定不影响)
function Animal() {
this.name = '米奇'
return 123 // return的是原始类型
}
let a = new Animal()
console.log(a); // 输出:Animal { name: '米奇' } ✅
console.log(a.name); // 输出:米奇 ✅
function Animal() {
this.name = '米奇'
return {b: 1} // return的是引用类型(对象)
}
let a2 = new Animal()
console.log(a2); // 输出:{b: 1} ✅(new绑定失效,返回return的对象)
console.log(a2.name); // 输出:undefined ✅(a2不再是Animal实例,没有name属性)
补充new的底层原理(对应你代码中的注释),帮你彻底理解:
-
创建一个空对象(var obj = {});
-
将构造函数的this绑定到这个空对象(Animal.call(obj));
-
给空对象添加属性(obj.name = '米奇');
-
将空对象的原型指向构造函数的原型(obj.__proto__ = Animal.prototype);
-
如果构造函数没有return,或者return的是基本类型,就返回这个空对象(实例);如果return的是引用类型,就返回这个引用类型。
四、特殊情况:箭头函数中的this
箭头函数是ES6新增的语法,它和普通函数最大的区别的是:箭头函数没有自己的this!箭头函数中的this,永远指向它“外层最近的非箭头函数”的this,而且一旦绑定,就无法修改(call、apply、bind也没用)。
function foo() {
var bar = () => {
this.a = 2 // 箭头函数没有this,指向外层非箭头函数foo的this
}
bar()
}
var obj = {
a: 1,
baz: foo
}
obj.baz() // 调用foo,foo的this指向obj(隐式绑定)
console.log(obj); // 输出:{a: 2, baz: ƒ} ✅(箭头函数的this指向obj,修改了obj.a)
// 验证:箭头函数this无法修改
var obj2 = {a: 3}
bar.call(obj2) // 试图用call修改this,无效
console.log(obj.a); // 依然是2 ✅
console.log(obj2.a); // 还是3 ✅
小提醒:箭头函数适合用在回调函数中(比如定时器、数组方法),可以避免this指向混乱;但不适合用在构造函数中,因为它没有自己的this,无法创建实例哦~
五、总结:this指向判断口诀
看完上面的规则,可能会觉得有点多,教大家一个简单的判断口诀,遇到this就按这个顺序来,永远不会错:
-
看函数是不是用new调用?是 → this指向实例;
-
看函数是不是用call、apply、bind调用?是 → this指向绑定的对象;
-
看函数是不是被上下文对象调用(对象.函数())?是 → this指向该对象;
-
看函数是不是箭头函数?是 → this指向外层最近的非箭头函数的this;
-
以上都不是 → 默认绑定,this指向window(严格模式下是undefined)。
其实this并没有那么难,核心就是“谁调用,指向谁”,再记住箭头函数和new的特殊情况,多练几个实例,就能轻松掌握。希望这篇文章能帮你吃透this,从此告别this踩坑烦恼,在JavaScript的路上越走越顺哦~
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。