各位前端小伙伴们,今天咱们来聊聊JavaScript中的this
关键字------这个让无数开发者抓耳挠腮的"小调皮"。它就像一个变脸大师,一会儿指向window
,一会儿指向对象,一会儿又指向实例...今天,咱们就来揭开它的神秘面纱!
为什么要有this?它存在的意义是啥?
首先,咱们得弄明白:为什么JavaScript要设计this
这么个玩意儿?答案很简单------为了让代码更优雅!
想象一下,如果没有this
,咱们得这样写代码:
javascript:d:\学习\深索\js\this\1.js
function identify(context) {
return context.name.toUpperCase()
}
function speek(context) {
var greeting = 'hello, I am ' + identify(context)
console.log(greeting)
}
var me = { name: 'Tom' }
speek(me) // 每次调用都得手动传context,多麻烦啊!
有了this
之后,咱们可以这样写:
javascript:d:\学习\深索\js\this\1.js
function identify() {
return this.name.toUpperCase()
}
function speek() {
var greeting = 'hello, I am ' + identify.call(this)
console.log(greeting)
}
var me = { name: 'Tom' }
speek.call(me) // 优雅多了!
this
就像一个隐形的传递者,默默地帮咱们传递对象引用,让代码更简洁、更易于复用。
this的绑定规则:五大门派各显神通
this
的指向并不是在定义时确定的,而是在调用时确定的。它有五大绑定规则,咱们一个个来看:
1. 默认绑定:无依无靠的this
当函数被独立调用时,this
默认指向全局对象(浏览器中是window
)。
javascript:d:\学习\深索\js\this\2.js
function foo() {
console.log(this) // window
}
foo() // 独立调用,this指向window
console.log(this) // 全局作用域中的this也是window
注意啦,在严格模式下,默认绑定的this
会变成undefined
,这是个小小的陷阱!
2. 隐式绑定:有"靠山"的this
当函数引用有上下文对象,并且被该对象调用时,this
就会绑定到这个上下文对象上。
javascript:d:\学习\深索\js\this\3.js
var a = 3
function foo() {
var a = 2
function bar() {
var a = 1
console.log(this.a) // 3,因为bar是独立调用,this指向window
}
bar()
}
foo()
上面这个例子,bar
虽然在foo
里面定义,但它是独立调用的,所以this
还是指向window
。那怎么让this
指向对象呢?
javascript:d:\学习\深索\js\this\4.js
var a = 1
function foo() {
console.log(this.a) // 2,因为是obj调用的foo
}
var obj = {
a: 2,
foo: foo
}
obj.foo() // 方法调用,this指向调用者obj
3. 隐式丢失:this的"背叛"行为
有时候,this
会"背叛"它的主人,这就是隐式丢失。最常见的情况是把对象的方法赋值给变量,然后独立调用这个变量。
javascript:d:\学习\深索\js\this\4.js
var a = 1
var obj = {
a: 2,
foo: function() {
console.log(this.a)
}
}
var bar = obj.foo // 这里只是把函数赋值给bar,没有调用
bar() // 独立调用,this指向window,输出1
另外,当一个函数被多层对象调用时,this
只会指向最近的那一层对象:
javascript:d:\学习\深索\js\this\4.js
var a = 1
var obj = { a: 2, foo: foo }
var obj2 = {
a: 3,
obj: obj
}
obj2.obj.foo() // this指向obj,输出2,而不是obj2
4. 显式绑定:强行指定this
咱们可以使用call
、apply
和bind
这三个方法,强行把this
绑定到指定的对象上。
javascript:d:\学习\深索\js\this\5.js
function foo(x, y) {
console.log(this.a, x + y)
}
var obj = { a: 1 }
// call方法:参数一个个传
foo.call(obj, 1, 2) // 输出:1 3
// apply方法:参数用数组包起来
foo.apply(obj, [1, 2]) // 输出:1 3
// bind方法:返回一个新函数,参数可以分两次传
const bar = foo.bind(obj, 2, 3)
bar(4) // 输出:1 9(2+3+4?不,bind的参数和调用时的参数会合并,2+3是bind传的,4是调用时传的)
这三个方法的区别在于:call
和apply
会立即执行函数,而bind
会返回一个新函数,需要再次调用。
5. new绑定:this的"认祖归宗"
当使用new
关键字调用构造函数时,this
会绑定到新创建的实例对象上。
javascript:d:\学习\深索\js\this\6.js
function Person() {
this.name = '超超'
this.age = 18
console.log(this) // 指向新创建的实例对象 {name: '超超', age: 18}
}
const p1 = new Person()
new
操作符做了什么呢?简单来说:
- 创建一个新对象
- 把这个新对象的
__proto__
指向构造函数的prototype
- 把构造函数的
this
指向这个新对象 - 如果构造函数没有返回对象,就返回这个新对象
箭头函数:this的"叛逆者"
ES6引入的箭头函数,对this
做了特殊处理------它根本就没有自己的this
!箭头函数里的this
,其实是外层作用域的this
。
javascript:d:\学习\深索\js\this\7.js
var a = 1
var obj = {
a: 2,
bar: function() {
const baz = () => {
console.log(this.a) // 2,因为baz的this是bar的this,也就是obj
}
baz()
}
}
obj.bar()
箭头函数的这个特性,特别适合解决回调函数中的this
问题。比如在定时器、事件监听器中,再也不用写const that = this
这样的代码了!
this的绑定优先级:谁的话更管用?
当多种绑定规则同时存在时,this
该听谁的呢?这里有个优先级排序:
new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
箭头函数比较特殊,它不遵循这些规则,它的this
由外层作用域决定,而且一旦确定就不能改变。
总结:this的"使用指南"
- 独立调用 :
this
指向window
(非严格模式)或undefined
(严格模式) - 方法调用 :
this
指向调用该方法的对象 call
/apply
/bind
调用 :this
指向指定的对象new
调用 :this
指向新创建的实例对象- 箭头函数 :
this
指向外层作用域的this
记住这些规则,遇到this
相关的问题时,先分析函数是怎么被调用的,再根据规则判断this
的指向,就能轻松解决大部分问题啦!
最后,给大家留个小练习:分析下面代码中this
的指向,欢迎在评论区分享你的答案~
javascript
var a = 1
const obj = {
a: 2,
foo: function() {
setTimeout(function() {
console.log(this.a) // 这里的this指向谁?
}, 0)
},
bar: function() {
setTimeout(() => {
console.log(this.a) // 这里的this又指向谁?
}, 0)
}
}
obj.foo()
obj.bar()
好啦,今天关于this
的分享就到这里。希望这篇文章能帮你更好地理解JavaScript中的this
关键字,让你在写代码时不再被它困扰!如果觉得有用,别忘了点赞收藏哦~