JavaScript 中的 this,到底指向谁?
你是否也写过这样的代码------
javascript
const obj = {
name: 'zhangsan',
say() {
console.log(this.name)
}
}
const bar = obj.say
bar() // ?
大部分人第一次看到时会猜"zhangsan",结果打印出来却是 undefined。
为什么?因为 this 永远取决于函数"被调用的方式" ,而不是定义的位置。
一、this 是什么?
this 是在函数执行时 自动绑定的一个对象引用。
它的值取决于调用方式 ,而非函数定义的位置。
简单说:
"谁调用我,我就指向谁(除非你强制改变我)。"
- 每一个函数都有自己的this,即使两个函数的指向相同,this本质上也不是同一个,this只在函数执行时产生
- this在函数预编译时产生,每一个this都代表一个函数的执行
二、四种 this 绑定规则
1. 默认绑定 ------ 独立调用,指向 window(或 undefined)
当函数被独立调用 时,this 默认指向全局对象。
在浏览器中是 window,在严格模式下是 undefined。
scss
function foo() {
console.log(this)
}
foo() // window
2. 隐式绑定 ------ 谁调用,this 就指向谁
当函数作为对象的方法被调用时,this 会指向该对象。
javascript
const person = {
name: 'Tom',
say() {
console.log(this.name)
}
}
person.say() // Tom
但是,如果"方法"脱离了对象独立执行,就会丢失隐式绑定,回到默认绑定规则:
csharp
const fn = person.say
fn() // undefined(严格模式下)
⚠️ 注意闭包的情况:(什么是闭包?函数执行时,被定义并且被抛出)
javascript
const obj = {
name: 'Alice',
outer() {
function inner() {
console.log(this.name)
}
inner()
}
}
obj.outer() // undefined,因为 inner 独立执行
可以用箭头函数解决:
javascript
const obj = {
name: 'Alice',
outer() {
const inner = () => console.log(this.name)
inner()
}
}
obj.outer() // Alice
3. 显式绑定 ------ call / apply / bind
你可以手动指定 this:
scss
function greet() {
console.log(this.name)
}
const user = { name: 'Lucy' }
greet.call(user) // Lucy
greet.apply(user) // Lucy
greet.bind(user)()// Lucy
区别:
call和apply立即执行;bind返回一个新函数,之后执行;apply传参用数组形式。
如果传入的是原始类型(如字符串),this 会被转换为包装对象;
如果是
null或undefined,则退回默认绑定。
4. new 绑定 ------ 构造函数调用
当函数通过 new 调用时,会创建一个新对象,并将 this 绑定到该对象上。
javascript
function Person(name) {
this.name = name
}
const p = new Person('ZhangSan')
console.log(p.name) // ZhangSan
底层过程相当于:
javascript
function Person(name) {
// var this = {}
this.name = name
// return this
}
如果构造函数显式返回对象,则返回该对象而不是 this:
javascript
function Foo() {
return { name: '自定义对象' }
}
console.log(new Foo()) // { name: '自定义对象' }
三、绑定优先级(最重要的判断规则)
| 绑定方式 | 示例 | 优先级 | 说明 |
|---|---|---|---|
| new绑定 | new Foo() |
最高 | 创建实例时绑定 |
| 显式绑定 | foo.call(obj) |
高 | 手动指定 |
| 隐式绑定 | obj.foo() |
中 | 谁调用指向谁 |
| 默认绑定 | foo() |
低 | 非严格模式 → window |
一句话记忆:
new > call/apply/bind > 对象调用 > 独立调用
四、箭头函数的特殊性
箭头函数没有自己的 this,它会继承外层作用域的 this 。
因此,所有四条绑定规则都对它无效。
javascript
const obj = {
name: 'Tom',
say: () => console.log(this.name)
}
obj.say() // undefined(继承自全局作用域)
但如果在函数作用域内使用箭头函数,则可以捕获父作用域的 this:
javascript
const obj = {
name: 'Tom',
outer() {
const inner = () => console.log(this.name)
inner()
}
}
obj.outer() // Tom
五、如何快速判断 this 指向?
可以按这个顺序判断:
- 函数是否通过
new调用?→ 指向实例; - 是否通过
call/apply/bind显式指定?→ 指向指定对象; - 是否作为对象属性被调用?→ 指向该对象;
- 否则 → 默认绑定(window / undefined)。
六、总结
this与函数定义无关,只与调用方式有关。- 四种绑定方式依次为:默认、隐式、显式、new。
- 箭头函数没有自己的 this,会继承外层作用域。
- 绑定优先级:new > 显式 > 隐式 > 默认。
📌 小贴士:闭包与 this 的区别
闭包让函数"记住变量的作用域";
this 决定函数"执行时的对象环境"。
两者常被混淆,但机制完全不同。
💡 写在最后
很多人初学时死记规则,不如换个角度思考:
this 不是"绑定函数"的机制,而是"绑定调用者"的机制。
只要你在执行前先问自己一句:
"这个函数是谁在调用?"
那你就永远不会搞错 this。