加深this的理解
关于this之前看过很多文章了,但是依然没有吃透,每次看的时候都有很多不懂的地方,😭相信很多人跟我一样被this混淆的
this是什么
this是一个关键字,它的值根据函数的调用方式而改变。
JavaScript中有以下几种场景:
- this在全局环境
- this在new对象实例中
- this在一个对象的方法中
- this在一个简单的function中
- this在一个箭头函数中
- this在监听事件中
- 使用了prototype的call/apply/bind
1.this指向全局对象中
当this直接在所有函数之外被调用,则在浏览器中表示window对象。在node中为global。例如:
console.log(this) //Window
console.log(this === window) //true
var bb = 1 //此处不用let
console.log(this.bb) //1
console.log(window.bb) //1
通常情况下,全局中很少使用this
2.this在new的对象实例中
当使用new关键字创建对象实例时,this指向实例。例如:
function Girl(name){
this.name = name
console.log(this)
console.log(this.name)
}
let girl1 = new Girl('Summer')
let girl2 = new Girl('Minya')
console.log(girl1) //Girl{name: "Summer"}, this.name = "Summer"
console.log(girl2) //Girl{name: "Minya"}, this.name = "Minya"
以上结果,this分别指向两个实例对象。
3.this在一个对象的方法中
let obj = {
afun(){
//对象里的一个method
console.log(this)
}
}
obj.afun() // obj
此时this指向object对象(obj)
由此再看下面的例子:
function Girl(name){
let obj = {
name,
getName(){
return this.name
}
}
return obj
}
let Summer = new Girl('Summer')
let Minya = new Girl('Minya')
console.log(Summer.getName()) //Summer
console.log(Minya.getName()) //Minya
在上述方法中,this执行obj对象
4.this在一个简单的function中
在简单的function中,this指向window,匿名函数也是类似的(在严格模式下,this默认为undefined)。例如:
function Girl(){
console.log(this) //Window
}
let obj = {
runGirl(){
Girl()
}
}
Girl() // Window
console.log(obj.runGirl()) // Window
(function() {
console.log(this) //Window
})()
以上this始终指向window,未受调用的上下文调用的影响。为什么?
再一个例子:
let obj2 = {
setIime() {
setTimeout(function(){
this.showInfo()
},0)
},
showInfo() {
console.log('123456')
}
}
obj2.setIime() //TypeError: this.showInfo is not a function
以上错误原因是,在setTimeout中,this指向window了,所以找不到showInfo。
修改如下:
//方法一,使用变量赋值
let obj3 = {
setIime() {
let _this = this
setTimeout(function(){
_this.showInfo()
},0)
},
showInfo() {
console.log('123456')
}
}
obj3.setIime() //123456
//方法二:使用es6的箭头函数(下文),将this指向上下文环境
5.this在一个箭头函数中
因为箭头函数没有this对象,所以在箭头函数中,this指向上下文环境,并不是window。例如:
let arrow = {
setTime() {
setTimeout(() => {
this.showInfo()
},0)
},
showInfo() {
console.log('this is a arrow function')
}
}
arrow.setTime()
改变this还有三种方式,使用bind、call、apply。后续继续…
6.this在监听事件中
在事件中,this指向触发事件的元素。例如:
let btn = document.querySelector('button')
button.addEventListener('click',function() {
console.log(this) // button元素
})
在一个方法中,创建事件监听器,并且在监听器中调用另一个方法。例如:
function EventTest(element) {
return {
clickListener() {
element.addEventLister('click',(event) => {
// 匿名函数
console.log(event.currentTarget) //element
this.showInfo() //other function
})
},
showInfo() {
console.log('other function')
}
}
}
以上监听器是一个匿名函数,无法删除监听器,可以修改回调函数为命名函数
如果需要在监听器中使用this引用对象,可以使用bind手动创建this的指向。例如:
function EventTest(element) {
return {
clickListener() {
this.listener = this.showInfo().bind(this)
element.addEventLister('click',this.listener)
},
showInfo(event) {
console.log('other function')
let elem = event.currentTarget
element.removeEventLister('click',this.listener)//删除监听事件
}
}
}
bind?好困惑啊…继续…
7.bind
bind是每个方法都存在的。它允许改变this的指向。bind接受任意数量的参数,并且返回绑定函数。例如:
function testBind() {
console.log(this)
}
let bindFunc = testBind.bind({name:'Summer'})
bindFunc() //{name: "Summer"}
bind传入的第一个参数成了this绑定的函数,其他参数则作为参数传给原始参数。例如:
function testBind(...args) {
console.log(this) // {name:'Summer'}
console.log(...args) // 2 3 4 5 6
}
let bindFunc = testBind.bind({name:'Summer'},2,3,4,5,6)
bindFunc()
注:bind不能用于箭头函数
call/apply也是将自己的第一个参数作为this的指向对象。例如:
function Meili() {
console.log(this.name);
}
var name = "Summer";
var obj = { name: "Minya" }
Meili(); // "Summer"
Meili.call(obj); // "Minya"
以上代码,通过call传递的参数,使得this指向第一个参数
9.检查this绑定对象的优先级
综上,this有4中绑定规:
- 默认绑定:this在简单的function中
- 隐式绑定:this在一个对象的方法中
- 显式绑定:使用了prototype的call/apply/bind
- new 绑定:this在new对象实例中
优先级:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
总结
this是JavaScript的一个重要基础,不能不会。
JavaScript主要有两种编码风格:面向对象编程(OOP)、函数式编程(FP)。
OOP是一种围绕创建对象的编程风格。大量使用this访问属性和方法。FP是一种函数调用执行的风格,基本不使用this。
最后,this是在函数被调用时发生的绑定,指向什么取决于函数在哪里被调用,而不是声明。