JavaScript 的 "this":从新手到轻松掌握
最近在学习 JavaScript 中的
this关键字,发现它真的是让人又爱又恨的存在。今天我想和大家分享我对this的理解,希望能帮助到同样在学习的同学。
一、为什么要用 this?
我们先看一个例子:
// 没有 this 的写法
function identify(context) {
return context.name.toUpperCase()
}
function speak(context) {
const greeting = 'Hello, I am ' + identify(context)
console.log(greeting)
}
const me = { name: 'Tom' }
speak(me) // Hello, I am TOM
每次调用都要显式传递 context 对象,代码显得冗长。而使用 this:
// 使用 this 的写法
function identify() {
return this.name.toUpperCase()
}
function speak() {
const greeting = 'Hello, I am ' + identify.call(this)
console.log(greeting)
}
const me = { name: 'Tom' }
speak.call(me) // Hello, I am TOM
this 提供了更优雅的方式来隐式传递对象引用,让代码更简洁、易于复用。
二、this 到底是什么?
简单来说,this 是一个代词,它的值取决于函数被调用的方式。就像中文里的"我",在不同人的嘴里指向不同的人。
// 在不同环境中,this 指向不同
console.log(this) // 全局环境下指向 window
const obj = {
name: 'Tom',
sayHi() {
console.log(this.name) // 这里的 this 指向 obj
}
}
三、this 的四大绑定规则
1. 默认绑定(独立函数调用)
当函数被独立调用时,this 指向全局对象(浏览器中是 window):
var a = 1
function foo() {
console.log(this.a) // 1
}
foo() // 相当于 window.foo()
注意:严格模式下,this 会是 undefined。
2. 隐式绑定(方法调用)
当函数作为对象的方法被调用时,this 指向该对象:
function foo() {
console.log(this.a)
}
const obj = {
a: 2,
foo: foo
}
obj.foo() // 2,this 指向 obj
但是注意隐式丢失的问题:
const obj = {
a: 2,
foo: function() {
console.log(this.a)
}
}
const bar = obj.foo // 只是赋值函数,没有调用!
bar() // undefined,此时 this 指向 window
3. 显式绑定(call/apply/bind)
我们可以强制指定 this 的指向:
function greet(greeting, punctuation) {
console.log(greeting + ', ' + this.name + punctuation)
}
const person = { name: 'Tom' }
// call - 立即执行,参数逐个传递
greet.call(person, 'Hello', '!') // Hello, Tom!
// apply - 立即执行,参数以数组传递
greet.apply(person, ['Hi', '!!']) // Hi, Tom!!
// bind - 返回新函数,稍后执行
const greetTom = greet.bind(person, 'Hey')
greetTom('...') // Hey, Tom...
4. new 绑定(构造函数调用)
使用 new 调用函数时,this 会绑定到新创建的对象:
function Person(name) {
this.name = name
this.sayHi = function() {
console.log('Hi, I am ' + this.name)
}
}
const tom = new Person('Tom')
tom.sayHi() // Hi, I am Tom
注意:如果构造函数有 return 语句:
- 返回基本类型,会被忽略
- 返回对象,则会替换掉 new 创建的对象
function Person() {
this.name = 'Tom'
return { name: 'Jerry' }
}
const p = new Person()
console.log(p.name) // Jerry,不是 Tom!
四、箭头函数的 this
箭头函数没有自己的 this,它的 this 继承自外层作用域:
const obj = {
name: 'Tom',
sayHi: function() {
// 普通函数,this 指向 obj
setTimeout(function() {
console.log('Hi, ' + this.name) // undefined,this 指向 window
}, 100)
}
}
const obj2 = {
name: 'Jerry',
sayHi: function() {
// 箭头函数,继承外层的 this
setTimeout(() => {
console.log('Hi, ' + this.name) // Hi, Jerry
}, 100)
}
}
五、优先级和特殊情况
四种绑定规则的优先级: new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定
function foo() {
console.log(this.a)
}
const obj1 = { a: 1, foo }
const obj2 = { a: 2, foo }
obj1.foo() // 1,隐式绑定
obj1.foo.call(obj2) // 2,显式绑定优先
总结
理解 this 的关键是记住:this 的指向在函数被调用时确定,而不是定义时。它就像个"变色龙",会根据不同的调用环境改变指向。
对于初学者,我的建议是:
- 先掌握四大绑定规则
- 多写代码实践,理解各种情况
- 善用箭头函数解决大部分问题
希望这篇文章对你有帮助!如果有任何疑问或指正,欢迎在评论区讨论~