一、this 本质是什么?
一句话:
this 不是在定义时决定的,而是在“调用时”决定的。
记住这句话非常关键。
二、this 的 5 种指向规则
1. 默认绑定(Default Binding)
函数独立调用时,this 指向全局对象(浏览器 window,Node.js global)
⚠️ 严格模式('use strict')下为 undefined
function test() {
console.log(this);
}
test(); // window / undefined(严格模式)
// 函数嵌套独立调用
function outer() {
function inner() {
console.log(this); // window(非严格)
}
inner();
}
浏览器下:
window
严格模式:
undefined
2. 隐式绑定(Implicit Binding)
函数作为对象的方法调用,this 指向调用该方法的对象
//隐式绑定
const obj = {
name: 'obj',
fn() {
console.log(this.name);
}
};
obj.fn(); // obj(调用者是 obj)
// 链式调用
const obj2 = { name: 'obj2', child: obj };
obj2.child.fn(); // obj(只看直接调用者 child)
this 指向:
obj
规则:
谁调用,this 指向谁
3. 隐式丢失
const obj = {
name: "Jake",
test() {
console.log(this);
}
};
const fn = obj.test;
fn();
输出:
原因:
函数已经脱离 obj 调用。
4. 显式绑定(Explicit Binding)
强制指定 this,通过 call / apply / bind
function say() {
console.log(this.name);
}
const user = { name: 'Alice' };
say.call(user); // Alice
say.apply(user); // Alice
const bound = say.bind(user);
bound(); // Alice
区别:
call/apply:立即执行,参数格式不同bind:返回新函数,永久绑定,不立即执行
| 方法 | 立即执行 | 返回值 |
|---|---|---|
| call | 是 | 函数结果 |
| apply | 是 | 函数结果 |
| bind | 否 | 新函数 |
5. new 绑定(new Binding)
用 new 调用函数(构造函数) ,this 指向新创建的空对象
function Person(name) {
this.name = name;
}
const p = new Person('Tom');
console.log(p.name); // Tom
等价于:
const obj = {}; // 1. 创建空对象
obj.__proto__ = Person.prototype; // 2. 链接原型
Person.call(obj, 'Tom'); // 3. this 绑定到 obj
return obj; // 4. 返回对象
this 指向:
新创建的对象
new 做了 4 件事:
- 创建空对象
- this 指向这个对象
- 执行构造函数
- 返回对象
6. 箭头函数绑定(Arrow Function Binding)
箭头函数没有自己的 this,它使用定义时外层作用域的 this,且绑定后无法修改(call / apply / bind 无效)
例如
const obj = {
name: "Jake",
test() {
const fn = () => {
console.log(this);
};
fn();
}
};
obj.test();
输出:
obj
三、this 优先级规则
优先级从高到低:
new 绑定
↓
显式绑定(call/apply/bind)
↓
隐式绑定
↓
默认绑定
四、经典面试题
题 1
var name = "global";
const obj = {
name: "Jake",
test: function () {
console.log(this.name);
}
};
obj.test();
答案:
Jake
题 2
var name = "global";
const obj = {
name: "Jake",
test: function () {
return function () {
console.log(this.name);
};
}
};
obj.test()();
答案:
global
因为是普通函数调用。
题 3(箭头函数)
var name = "global";
const obj = {
name: "Jake",
test: function () {
return () => {
console.log(this.name);
};
}
};
obj.test()();
答案:
Jake
因为箭头函数继承 test 的 this。
五、万能判断口诀
判断 this 只看:
- 函数怎么调用
- 有没有 new
- 有没有 call/apply/bind
- 是不是箭头函数
不要看函数在哪定义!
六、一句话总结
this 永远由“调用方式”决定,而不是定义位置决定。
七、this进阶面试题目
🧠 题 1
var a = 10;
function test() {
console.log(this.a);
}
const obj = {
a: 20,
test,
};
obj.test();
打印结果:
编辑
👉 隐式绑定,谁调用指向谁。
🧠 题 2
var a = 10;
function test() {
console.log(this.a);
}
const obj = {
a: 20,
test,
};
const fn = obj.test;
fn();
打印结果:
10 (浏览器)
undefined(严格模式)
👉 隐式丢失。
🧠 题 3
function foo() {
console.log(this);
}
foo.call(null);
打印结果
非严格模式:
window严格模式:
null
👉 null / undefined 会被默认绑定替代(非严格)。
🧠 题 4
function foo() {
console.log(this.name);
}
var name = "global";
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
foo.call(obj1);
foo.call(obj2);
打印结果:
编辑
🧠 题 5(bind 陷阱🔥)
function foo() {
console.log(this.name);
}
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
const fn = foo.bind(obj1);
fn.call(obj2);
打印结果:
👉 bind 绑定后,call 改不了。
优先级:
new > bind > call
🧠 题 6(new + bind)
function Foo(name) {
this.name = name;
}
const obj = { name: "obj" };
const Bar = Foo.bind(obj);
const p = new Bar("Jake");
console.log(p.name);
console.log(obj.name);
打印结果:
解说:
- new 优先级最高
- this 指向新对象
- bind 失效
🧠 题 7(箭头函数)
var name = "global";
const obj = {
name: "obj",
foo: () => {
console.log(this.name);
}
};
obj.foo();
打印结果:
编辑
解说:
- 箭头函数没有自己的 this
- 继承外层(全局)
🧠 题 8(箭头函数嵌套)
var name = "global";
const obj = {
name: "obj",
foo() {
return () => {
console.log(this.name);
};
}
};
obj.foo()();
打印结果:
解说:
箭头函数继承 foo 的 this。
🧠 题 9(setTimeout)
var name = "global";
const obj = {
name: "obj",
foo() {
setTimeout(function () {
console.log(this.name);
}, 0);
}
};
obj.foo();
答案:
解说:
setTimeout 里的普通函数默认绑定。
🧠 题 10(终极题🔥🔥🔥)
var name = "global";
const obj = {
name: "obj",
foo: function () {
console.log(this.name);
return function () {
console.log(this.name);
};
}
};
obj.foo()();
答案
解说
obj.foo() → 隐式绑定 → obj
返回普通函数 → 默认绑定
切记口诀
判断 this 永远看:
- 是不是 new?
- 有没有 bind?
- 有没有 call/apply?
- 是不是对象调用?
- 是不是箭头函数?
优先级
new
↓
bind
↓
call/apply
↓
隐式
↓
默认
🌱 写在最后
写作这些技术文章的过程,也是我重新梳理知识体系的过程。
编程不仅是实现功能,更是一种思考方式。希望这篇文章不仅能帮你解决眼前的问题,更能激发你对[某个技术点]的更深层思考。
保持好奇,保持热爱。