前言
如果面试官让你用一句话总结this的指向,你会怎么总结。广泛流传的一句话是:
谁调用它,
this就指向谁。
严格来说,这句话的描述不够规范也不完全准确。对this指向的分析离不开执行上下文。因此,本文我们将结合执行上下文对this的各种使用场景进行分析。
全局环境下的this
例题一
1 | function f1 () { |
函数f1和f2都在全局环境下被调用或者说没有直接调用者,在非严格模式下this指向了调用它window对象,但在严格模式下this指向undefined。
例题二
1 | const foo = { |
1 |
|
在此题中,我们先在全局环境下调用foo.fn(),而函数fn在foo对象中被调用。此时,this指向的是最后调用它的对象foo,this.bar就相当于foo.bar。
例题四
1 | const person = { |
由例题三的分析,此题的结果也很明了,最后调用函数fn的对象为brother,因此this.name就相当于brother.name
例题五
思考下面比较复杂的情况
1 | const o1 = { |
调用o1.fn()时,this指向o1对象,这个没什么好说的。
调用o2.fn()时,this也指向最后调用它的o1。
调用o3.fn()时,函数fn先被赋值为函数o1.fn,然后再调用赋值后的函数fn。此时情况就还原为了例子一。
bind/call/apply 改变 this 指向
先举个简单例子
例题六
1 | const foo = { |
利用call改变this指向,使this指向bar对象,最后打印bar.name。
1 |
|
显然,对于oop比较熟悉的人立马就知道这里打印的就是this.bar的值。
当new操作符调用构造函数时,会对this指向造成什么影响呢?我们简单说一下new操作符的工作就是:
- 创建一个新对象。
- 将构造函数的
this指向这个新对象。 - 为这个对象添加属性、方法等。
- 最终返回新对象。
箭头函数与this
这个也是我们在使用箭头函数时要注意的问题。
例题八
1 | const foo = { |
这里由于产生了闭包,this指向了window,与我们原先的想法有差异。为了能让this指向调用fn函数的foo对象,我们可以使用箭头函数来实现。
1 | const foo = { |
此时,this指向最近的函数即fn的this指向。
this的优先级
对this的绑定我们通常分为两类
- 显式绑定,如
call、apply、bind、new对this的绑定。 - 隐式绑定,如根据调用关系确定的
this指向。
那么显示绑定和隐式绑定谁的优先级更高呢?
例题九
1 | function foo (a) { |
上例比较了对象调用与call的优先级,根据打印的结果为2,1可以得知:call和apply的显式绑定的优先级要高于隐式绑定。
例题十
1 | function foo (a) { |
上例使用bind将bar函数中this绑定为obj1对象,因此执行bar(2)后,obj1.a的值为2。
此时,若将bar函数当做构造函数,new一个对象时,this将绑定到新创建的对象上。
1 | var baz = new bar(3) |
如上例,结果将输出3。
由此可以得到结论,同样是显示绑定new的优先级比bind高。
例题十一
1 | function foo() { |
首先用call将this绑定到obj1,此时bar(引用箭头函数)的this也会绑定到obj1,箭头函数的绑定无法被修改。因此,最后将打印2。
后记
通过上面的例子,我们可以得知:
this的指向,是在调用函数时根据执行上下文所动态确定的。
当然,要完全解决this指向问题,光靠上面几个例子是不够的,还需要自己多写多看多做总结,才能把知识点融会贯通。