一、this 是什么?
很多初学者对 this 的理解是 "指向自己",这是完全错误的!
this 的本质是 动态绑定 的指针,指向 调用它的那个对象。就像打电话:
- 你直接拨打(直接调用函数),接电话的是全局对象(
window) - 你通过朋友转接(通过对象调用),接电话的是那个朋友(对象)
- 你强制指定转接(
call/apply),接电话的是你指定的人
记住这句话:this 永远指向最后调用它的那个对象。
二、四大绑定规则
判断 this 指向,按优先级从高到低:
1. new 绑定(最高优先级)
使用 new 调用函数时,this 会绑定到新创建的对象上:
function Person(name) {
this.name = name; // this → 新对象
}
const p = new Person('小明');
2. 显式绑定(call/apply/bind)
通过 call, apply, bind 强制指定 this:
function sayHi() { console.log(this.name) }
const obj = { name: '小红' };
sayHi.call(obj); // 输出:小红
3. 隐式绑定(对象方法调用)
函数作为对象属性被调用时,this 指向该对象:
const cat = {
name: '喵喵',
eat: function() { console.log(this.name + '吃鱼') }
};
cat.eat(); // 喵喵吃鱼
隐式丢失的坑!
当函数被赋值给变量后调用,this 会丢失原对象,指向全局:
const eat = cat.eat;
eat(); // undefined吃鱼(严格模式报错)
4. 默认绑定(最低优先级)
独立函数调用时,this 指向全局(非严格模式)或 undefined(严格模式):
function foo() { console.log(this) }
foo(); // window(浏览器环境)
三、箭头函数——打破规则的特例
箭头函数没有自己的 this,会 继承外层作用域的 this,且无法被修改:
const obj = {
name: '外层',
func: function() {
const arrow = () => console.log(this.name);
arrow();
}
};
obj.func(); // 外层(箭头函数继承 func 的 this)
对比普通函数:
const obj = {
func: function() {
setTimeout(function() {
console.log(this); // 普通函数调用,this指向window
}, 100);
setTimeout(() => {
console.log(this); // obj(箭头继承外层 func 的 this)
}, 200);
}
};
四、改变 this 指向的三种方法
在 JavaScript 中,this 的指向是动态的,但我们可以通过 显式绑定 强制指定它的值。显式绑定有三种方法:call、apply 和 bind,它们的作用和用法各有特点。
1. call:立即调用,参数逐个传递
call 的作用是 立即执行函数,并明确指定 this 的值,同时将参数逐个传递。比如:
function greet() { console.log(`Hello, ${this.name}`) }
const obj = { name: '小明' };
greet.call(obj); // Hello, 小明
call 的底层原理是临时将函数挂载到目标对象上,然后通过对象调用函数。
2. apply:立即调用,参数数组传递
apply 和 call 的功能几乎一样,唯一的区别是 参数传递方式:apply 接受一个数组作为参数。比如:
function add(a, b) { console.log(a + b) }
add.apply(null, [1, 2]); // 3
apply 常用于参数数量不确定的场景,比如将数组元素作为参数传递给函数。
3. bind:返回新函数,延迟调用
bind 的作用是 创建一个新函数,并将 this 永久绑定到指定的值。与 call 和 apply 不同,bind 不会立即执行函数,而是返回一个绑定了 this 的新函数。比如:
function greet() { console.log(`Hello, ${this.name}`) }
const obj = { name: '小明' };
const boundFunc = greet.bind(obj);
boundFunc(); // Hello, 小明
bind 还可以提前绑定部分参数,实现函数柯里化。
总结:
call和apply是立即调用,区别在于参数传递方式。bind是延迟调用,返回一个新函数。- 显式绑定是 JavaScript 中非常强大的工具,熟练掌握后可以大幅提升代码的灵活性和可读性!
五、实战练习题
题目1:
var name = '全局';
const obj = {
name: '对象',
func: function() {
setTimeout(function() {
console.log(this.name);
}, 0);
}
};
obj.func(); // 输出?
题目2:
const button = document.querySelector('button');
button.addEventListener('click', function() {
setTimeout(() => {
console.log(this); // this 指向?
}, 100);
});
题目3:
function Foo() {
this.value = 1;
setTimeout(function() {
this.value++;
}, 100);
}
const obj = new Foo();
setTimeout(() => console.log(obj.value), 200); // 输出?
答案解析:
-
输出:全局
setTimeout的回调是普通函数,this默认指向window
-
指向 button 元素
- 外层
function的this由addEventListener绑定为触发元素,箭头函数继承
- 外层
-
输出:1
setTimeout中的this指向window,未修改obj.value
总结口诀:
- 普通函数看调用,谁点出来就是谁
- 箭头函数看外层,定义位置定终身
- new/显式优先级高,四大规则要记牢
理解 this 的关键在于分析 函数的调用方式,多写多练才能形成直觉!