this指向问题
概要
- 为什么使用this?
- this指向什么?
- this绑定规则
- this案例理解
为什么使用this
在某些函数和方法的编写中,this指向可以让我们更加便捷的方式来引用对象。在某些api的设计中,代码更加简洁和易复用
比如当我们调用一个对象中的属性,可通过obj.key来实现,但如果我们修改了对象名则内部所有调用修需要修改。
例子:代码中我们需要修改对象名obj为info时,内部函数中的所有obj都需修改为info
所以我们需要使用this来解决这个问题,修改对象名不影响对象内部函数的调用
this指向什么
首先全局作用域中,this在浏览器测试中指向window
但实际开发中,this的使用一般是在函数中调用
js函数在执行时,会创建一个执行上下文。这个执行上下文记载函数的调用栈、调用方式、传参等等,this也是其中一个重要的属性。
例子:当我们定义一个函数,使用三种方式调用
- 函数在调用时,JavaScript会默认给this绑定一个值
- this的绑定和定义的位置没有关系
- this的绑定和调用方式以及调用的位置有关系
- this是在运行时被绑定的
this绑定规则
默认绑定
默认绑定,一般用于独立函数
- 独立函数直接调用
- 调用链,所有函数的this都没有绑定到某个对象上
- 将函数作为参数传入
foo()的调用是将整个函数作为函数传入foo,所以函数调用是在独立函数中,this指向全局
foo3()的调用的入参是obj.foo1(),所以入参已经是调用后的函数,foo1函数的调用是在obj中实现的,this指向obj对象
隐式绑定
通过某个对象调用,即****它的调用位置中,是通过某个对象发起的函数调用
- 通过对象调用
- 对象obj2调用对象obj1再调用函数,函数实际调用位置在obj1上
- 隐式丢失
将obj1.foo赋给bar时并未对函数进行调用,函数的调用是在bar中进行,此时this指向window
显式绑定
隐式绑定中,需要对象自身拥有一个对应的属性。
- call和apply
当我们在对象中没用定义这个属性,但又需要这个对象来调用函数时,我们需要用到call/apply方法来实现
- call和apply都是Function原型上的方法
- 两个方法的功能类似,都是让调用函数的this绑定到特定的对象上
- 两个方法第一个参数都是接收要绑定的对象,第二个参数为函数入参,call为参数列表,apply为参数数组
call和apply显式绑定
- bind函数
当我们需要将一个函数始终绑定到一个对象上时
- 使用bind写一个辅助函数
此时的bar函数调用时,this指向始终与obj对象绑定
- 使用Function.prototype.bind
- 内置函数
当我们使用JavaScript的一些内置函数或者第三方库的内置函数时,这些函数要求我们传入另一个函数,我们并不会显式调用这些函数,而JavaScript或者第三方库会帮助我们执行,这些函数的this时如何绑定的呢?
- setTimeout
setTimeout传入的函数的this指向window
- 数组forEach
直接调用时,函数的this指向window
forEach方法的第二个参数可接收要执行改方法的数组,传入第二个参数后可将this的指向绑定到传入的数组上
new绑定
new关键字来调用一个函数时,会进行以下操作:
- 创建一个新的对象
- 将对象的原型指向构造函数的prototype(此时this指针完成绑定)
- 执行构造函数
- 返回对象
规则优先级
- 首先默认规则的优先级最低,即默认绑定等
- 显式绑定的优先级高于隐式绑定
- new绑定的优先级高于显示绑定
即优先级:new绑定>显式绑定>隐式绑定>默认绑定
特殊的this指向
- 忽略显式绑定
在显式绑定时,如果传入一个null或者undefined,则这个显式绑定会被忽略,使用默认规则
- 间接函数引用
赋值obj2.foo = obj1.foo结果为foo函数,被直接调用则为window
- 箭头函数
es6中新增的箭头函数不使用this绑定的规则,而是根据外层作用域来决定this
this引用会在上层作用域中查找对应的this
案例理解
- 案例一
- 案例二
- 案例三