前言
在上一篇文章中,在我们聊的 JavaScript原型对象(prototype)、实例和构造函数(constructor)的关系中,为什么构造函数(constructor) 和类的constructor函数中定义属性需要使用 this?今天我们就来聊一聊 JavaScript 中的this指向问题。
// 构造函数,这里定义使用的this指向哪里?
function MyWorld1(name1, name2, name3) {
this.name1 = name1
this.name2 = name2
this.name3 = name3
}
// 类中constructor函数使用的this又指向哪里?
class Myworld2 {
constructor(name, grade, figure) {
this.name = name
this.grade = grade
this.figure = figure
}
// 实例方法
InstanceMethod() {
console.log(`${this.name}触发了实例方法`)
}
// 静态方法
static StaticMethod() {
console.log(`${this.name}触发了静态方法`)
}
}
一、this的指向
在 JavaScript 中,this 的指向是动态绑定的,取决于函数的调用方式,而非定义的位置。
二、核心规则(按优先级排序)
1、new 绑定
使用 new 调用构造函数(constructor)时,this 指向新创建的对象:
// 构造函数,这里定义使用的this指向哪里?
function MyWorld1(name1, name2, name3) {
this.name1 = name1
this.name2 = name2
this.name3 = name3
console.log('MyWorld1的this: ', this)
}
const myworld1 = new MyWorld1('我不叫菜只因', '小美', '王刚')
console.log('myworld1实例: ', myworld1)
输出结果中明确可以看到:
1. MyWorld1 构造函数中的 this 在被 new 调用后,就指向了新创建的实例对象 myworld1。
2、显示绑定
通过 call()、apply() 或 bind() 强制指定 this:
function MyWorld(grade) {
this.grade = grade
console.log('MyWorld1函数中的this: ', this)
}
const obj = {
name1: '我不叫菜只因',
name2: '小美',
name3: '王刚',
id: 'obj'
}
MyWorld.call(obj)
MyWorld.call(obj, '等级0')
输出结果可以看出 call() 方法的使用,至于 apply() 和 bind() 方法的详细使用,读者可以私下自行查阅,这里不做过多的描述。
3、隐式绑定
函数作为对象方法调用时,this 指向调用它的对象:
const MyWorld3 = {
name: '我不叫菜只因',
Jump() {
console.log('MyWorld3.Jump的this指向: ', this, this.name)
}
}
MyWorld3.Jump()
输出结果可以看出对象下的函数的this指向是该对象本身
4、默认绑定
独立函数调用时,this 指向全局对象(浏览器中为 window,Node.js 中为 global)。严格模式下为 undefined:这边就展示浏览器下的情况
function MyWorld4() {
console.log('MyWorld4的this指向: ', this)
}
MyWorld4()
输出结果为浏览器的 window
三、特殊场景
1、箭头函数
箭头函数没有自己的 this,继承外层作用域的 this:
const Obj = {
name: 'Obj',
grade: '等级1',
id: 111,
MyWorld6: () => {
console.log('MyWorld6的this指向: ', this) // 箭头函数的this指向外层的this
},
MyWorld7() {
console.log('MyWorld7的this指向: ', this) // 普通函数的this指向调用它的对象
},
}
console.log('外层的this指向: ', this)
Obj.MyWorld6()
Obj.MyWorld7()
输出结果
2、回调函数
回调中的 this 由调用方式决定,常见丢失绑定问题:
const user = {
id: 'user',
name: '用户',
Jump() {
setTimeout(function() {
console.log('回调函数为普通函数的this指向: ', this) // 默认绑定到 window,丢失user
}, 100)
},
Squat() {
setTimeout(() => {
console.log('回调函数为箭头函数的this指向: ', this) // 继承user的this
}, 100)
}
}
user.Jump()
user.Squat()
输出结果
3、DOM事件处理器
事件处理函数中的 this 指向触发事件的 DOM 元素:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
</head>
<body>
<button id="btn">按钮</button>
<script>
const btn = document.getElementById('btn')
btn.addEventListener('click', function() {
console.log('DOM元素事件处理函数下的this指向: ', this)
})
// 不能使用箭头函数,箭头函数会使得 this 指向 window
btn.addEventListener('click', () => {
console.log('DOM元素事件处理函数下的this指向: ', this)
})
</script>
</body>
</html>
输出结果
四、关键注意事项
- 绑定丢失问题:方法被赋值给变量时可能丢失 this:
const Obj = {
name: 'Obj',
grade: '等级1',
id: 111,
MyWorld6: () => {
console.log('MyWorld6的this指向: ', this) // 外层的this是window
},
MyWorld7() {
console.log('MyWorld7的this指向: ', this)
},
}
const myworld = Obj.MyWorld7
myworld()
输出结果
- 严格模式影响:独立调用函数中 this 为 undefined,避免意外修改全局对象。
- bind的永久性:bind() 创建的绑定不可覆盖(优先级最高)。
- 异步上下文:Promise()、async/await 不影响 this 的规则,仍由调用方式决定。
五、总结表
调用方式 | this指向 | 示例 |
---|---|---|
new Constructor() | 新创建的对象 | new Person() |
fn.call(obj) | 显示指定的对象 | MyWorld.call(obj, '等级0') |
obj.method() | 调用方法的对象 | user.Squat() |
独立函数调用 | 全局对象/ undefined (严格) | MyWorld() |
箭头函数 | 外层作用域的 this | () => { ... } |
DOM事件处理 | 触发事件的DOM元素 | button.onclick = fn |
理解 this 的关键在于分析函数的调用方式,而非是定义的位置。