JS中的this
前言
🤟先来了解一下this的起源:
写函数时,在还不知道对象的名字(对象还没有声明)时拿到一个对象的属性,可以怎么做呢? 第一种:使用形式参数,调用函数的时候再传入 ,python使用了这个方法
let person = {
name: 'moji',
sayHi(p){
console.log(`你好,我叫` + p.name)
}
}
person.sayHi(person)
这显然和 JS 中对象调用函数的方式不一样,JS中是直接person.sayHi()
第二种:JS解决方法是,在每个函数里加入了this,可以使用this来调用还不知道名字的对象的属性值,在被对象调用时,this里的值就是对象的地址,使用this来获取对未知对象的引用。
let person = {
name: 'moji',
sayHi(this(省略)){
console.log(`你好,我叫` + this.name)
}
}
person.sayHi()
person.sayHi()会隐式地把 person(person 是个地址) 作为 this 传给 sayHi,这样,每个函数都能用 this 获取一个未知对象的引用了。
🤏简单来说,this就是最终调用函数的对象
ps:函数里不给其他条件,this默认指向window,箭头函数自己没有this,函数外面的this就函数里调用的this,call也无法使用
如何确定this
最重要的是this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用!
方法一:转换代码
参考博客
把函数调用的方式变为使用call
调用的方式
func.call(context, p1, p2)
this,就是上面代码中的 context,它是执行上下文的一个属性。执行上下文是当一个函数被调用时,被创建用于记录函数在哪里调用(调用栈),函数的调用方式,传入的参数等信息。
例如:
func(p1, p2) 等价于
func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等价于
obj.child.method.call(obj.child, p1, p2)
如果传的 context 是 null 或 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)
方法二:使用《你不知道的JavaScript(上)》中的准则
- 首先,需要找到这个函数的直接调用位置
- 找到之后就可以顺序应用下面四条规则来判断
this
的绑定对象
- 由new调用——绑定到新创建的对象
- 由call、apply、bind调用——绑定到指定的对象
- 由上下文对象调用——绑定到那个上下文对象(可以通过上面的转换代码的方式判断上下文对象)
- 默认:在严格模式下绑定到undefined,否则绑定到全局对象
一些特例
[ ] 语法
function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 这里面的 this 又是什么呢?
我们可以把 arr[0]()
想象为arr.0()
,虽然后者的语法错了,但是形式与转换代码里的 obj.child.method(p1, p2)
对应上了,于是就可以愉快的转换了:
arr[0]()
假想为 arr.0()
然后转换为 arr.0.call(arr)
那么里面的 this 就是 arr 了 :)
箭头函数
箭头函数里并没有 this,箭头函数会继承外层函数调用this绑定,如果在箭头函数里看到 this,直接把它当作箭头函数外面的 this 即可。
call()、apply()、bind()用法
可以使用call / apply / bind
来强制指定 this 的值
call()和apply()
call 方法第一个参数也是作为函数上下文的对象,后面传入的是一个参数列表,而不是单个数组。
apply 方法传入两个参数:一个是作为函数上下文的对象,另外一个是作为函数参数所组成的数组。
作用:
-
改变this的指向
-
调用函数
person.sayHi()=person.sayHi.call(person) ,不需要传this时,使用undefined来占位
bind()
它和 call 很相似,接受的参数有两部分,第一个参数是是作为函数上下文的对象,第二部分参数是个列表,可以接受多个参数。使用bind来绑定this,让this不再改变
面试题
参考
关于this的两道面试题和三篇文章