一. this 的五种绑定形式
常见的
this绑定
有五种形式:默认绑定 、隐式绑定 、显式绑定 、new绑定 、箭头函数绑定
<1> 默认绑定
默认绑定
可以理解为函数调用时无任何调用前缀的情景
,默认绑定时this指向全局对象 (非严格模式)
注意:
- 在严格模式下,默认的 this 绑定指向 undefined
- 但如果是在非严格模式下调用不在严格模式下的函数,并不会影响 this 指向!!!
<2> 隐式绑定
(1)
隐式绑定
,可以理解为函数调用时,前面存在调用它的对象,那么this就会隐式绑定到这个对象上
。且如果函数调用前存在多个对象,this指向距离调用自己最近的对象。
- 如上,输出的是
undefined
,因为 obj 对象中并没有 name 属性,它的原型链上也没有!!!
(2) 隐式丢失的情况:
隐式绑定丢失
最常见的就是两种情况:作为参数传递和变量赋值
- 作为参数传递
如上,传进去的只是一个函数,但该函数并没有与 obj 形成绑定关系,只是通过 obj 访问该函数,并把该函数作为参数传递了而已!!!
- 变量赋值
如上,跟参数传递本质是一样的,只是把该函数赋值给了一个变量,并没有形成绑定关系!!
(3) 补充: 作用域链与原型链的关系:
-
作用域链
javascript- 当访问一个变量时,解释器会先在当前作用域中查找标识符,如果没有找到的话就去父作用域 中找,作用域链顶端是全局对象 window ,如果 window 对象上没有该属性,就会报错!!
-
原型链
javascript- 当访问一个对象的某个属性时,首先会查找当前的对象,如果没有就顺着该对象的原型链 找,原型链的顶端是 Object 对象的原型 ,也就是 null ,如果没有找到的话,会返回 undefined,而不是报错!!!
<3> 显式绑定
(1)
显式绑定
可以理解为通过call、apply以及bind方法改变this的行为。在js中,当我们调用一个函数时,我们习惯称之为函数调用
,函数处于一个被动的状态;而call与apply让函数从被动变主动,函数能主动选择自己的上下文,所以这种写法我们又称之为函数应用
。
注意:
- 如果在使用call之类的方法改变this指向时,
指向参数提供的是null或者undefined,那么 this 将指向全局对象。
(2) 补充:call、apply、bind的区别:
-
.call、apply与bind都用于改变this绑定,但
call、apply
在改变this指向的同时还会执行函数 ,而bind
在改变this后是返回一个全新的boundFunction绑定函数,这也是为什么上方例子中bind后还加了一对括号 ()的原因。 -
【重点】 bind属于硬绑定 ,返回的 boundFunction 的 this 指向无法再次通过bind、apply或 call 修改;call与apply的绑定只适用当前调用,调用完就没了,下次要用还得再次绑。
-
call与apply功能完全相同,唯一不同的是call方法传递函数调用形参是以散列形式 ,而apply方法的形参是一个数组。在传参的情况下,call的性能要高于apply,因为apply在执行时还要多一步解析数组。
<4> new 绑定
new绑定
可以理解为使用构造函数 new 出来一个新的实例对象,构造函数体内的this将指向新对象echo上(可以抽象理解为新对象就是this)
<5> 箭头函数绑定【箭头函数的 this 绑定成功后也无法再通过三个方法修改,除了直接改变其所在作用域的 this 指向】
箭头函数
内部是没有 this 的,箭头函数内部的 this 指向完全取决于外层作用域中的 this 指向。外层作用域或函数的this指向谁,箭头函数中的this便指向谁。如果外层作用域是 window ,那箭头函数内部的 this 就指向全局对象 window。
如上,bar 指向 fn 函数执行完后返回的箭头函数,而执行 bar ,就是执行返回的箭头函数。因为该箭头函数外部作用域的函数 fn 的作用域,而该作用域中的 this 指向 onj1 ,因为函数 fn 通过 .call显式绑定与 obj1 相绑定了!!
注意:
- 箭头函数this还有一个特性,那就是
一旦箭头函数的this绑定成功,也无法被再次修改
,有点硬绑定的意思。但是我们还是可以通过修改外层函数this指向达到间接修改箭头函数this的目的。
<6> this 绑定的优先级
-
显式绑定 > 隐式绑定 > 默认绑定
-
new 绑定 > 隐式绑定 > 默认绑定
因为 new绑定和显示绑定不会同时出现(同时出现会报错),就没有两者的比较