JavaScript中的this解析

一张图放在前面

this绑定

默认绑定

独立函数调用时遵循默认绑定的原则。一般情况下若无其他规则出现则默认将this绑定到全局对象上。

1
2
3
4
5
6
function foo(){
var a = 3;
console.log(this.a)
}
var a = 2;
foo() // 2

隐式绑定

若调用位置有上下文对象就遵循隐式绑定(如example.foo())

1
2
3
4
5
6
7
8
function foo(){
console.log(this.a)
}
var obj = {a:2, foo:foo}
var a = 'global'
obj.foo() // 2
var bar = obj.foo
bar() // 'global

如图。之前我们直接调用foo函数时因为其具有上下文对象,所以正确打印,this此时正指向此对象(obj)。
可是之后的bar为什么又无法正确打印出对应的结果呢?
首先我们看,题目中用一个变量bar存储了obj.foo。所以为完成此赋值操作引擎会对obj.foo进行RHS查找,并找出其对应的键foo。继而再对值foo进行RHS查找,得到foo函数。
可是此时对其进行调用时,由于无上下文对象(bar本身的调用并不是靠”.“出来的)所以就造成了隐式丢失。故采取默认绑定规则,绑定在全局的a上。
或者我们换一种说法:bar引用的时foo函数本身。因而此时的bar其实是一个不带任何修饰的函数调用,自然采取默认绑定规则。

Ps:隐式丢失后会默认绑定在window

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a = "global";
function afun(){
function foo(){
console.log(this.a)
}
var obj = {a:2, foo:foo}
var a = 'innerglobal'
obj.foo() // 2
var bar = obj.foo
bar() // 'global
}
afun()
2
global

这题怎么解决

上面我们讲到了,隐式绑定的规则说通俗一点就是必须用”.“操作符将其函数与相应对象绑定。所以我们改成这样就可以啦

1
2
3
4
5
6
7
8
function foo(){
console.log(this.a)
}
var obj = {a:2, foo:foo}
var a = 'global'
obj.foo() // 2
var bar = obj
bar.foo() // 2

此例中前半部分都一样。一直进行RHS查找,直到将obj的调用地址赋给bar。也就是说bar此时具有了一个obj的引用。在此后通过.操作符操作时就不会再发生上面所说的隐式丢失了.

原理

对象属性引用链只有上一层或者说最后一层再调用位置中起作用

参数传递所引起的隐式丢失
1
2
3
4
5
6
7
8
9
function foo(){
console,log(a)
}
function dooFoo(fn){
fn() // 调用位置
}
var obj = {a:2, foo:foo}
var a = 'global'
dooFoo(obj.foo) // 'global'

我们分析一下。作为参数传递进dooFooobj.foo,引擎会对其进行RHS搜索。不信我们删掉foo看一下
在这里插入图片描述

喏,这个ReferenceError的报错就能充分说明问题了。

不成功的RHS引用会导致抛出 ReferenceError 异常。

不成功的 LHS 引用会导致自动隐式地创建一个全局变量(非严格模式下)。该变量使用 LHS 引用的目标作为标识符,或者抛 出 ReferenceError 异常(严格模式下)。

既然是RHS那么我们会得到一个foo函数的引用。但是此引用是脱离了上下文的,自然会发生隐式丢失。

JavaScript的内置库函数也一样,所以少用这种传入参数的方式,容易造成隐式丢失

显式绑定

隐式绑定这种由JavaScript内部机制造成的宫斗剧一般的勾心斗角显然不适合我这种单纯的boy。而正好有另一种绑定方式,简单粗暴易懂,让人一眼看出this作用域。

apply、call

在用此方法之前我建议好兄弟们去看一下这俩函数的相关内容

1
2
3
4
5
function foo(){
console,log(a)
}
var obj = {a: 2}
foo.call(obj) // 2

但是显式绑定仍然无法解决丢失绑定问题

硬绑定

创建一个可以重复使用的辅助函数
1
2
3
4
5
6
7
8
9
10
11
12
function foo(something){
console.log(this.a, something)
return this.a + something
}
function bind(fn, obj){
return function(){
return fn.apply(obj, arguments)
}
}
var obj = {a:2}
var b = bar(3) // 2 3
console.log(b) // 5

如图,我们每次在bind函数上将传入的函数硬性绑定在其对象上,如此一来无论如何调用bar,都会手动在obj上调用fn

Function.prototype.bind
1
2
3
4
5
6
7
function foo(something){
console.log(this.a, something)
return this.a + something
}
var obj = {a:2}
var b = bar(3) // 2 3
console.log(b) // 5

bind()会返回一个硬编码的新函数,它会把你指定的参数设置为this的上下文并调用原始函数

API调用参数指定this

一些函数会提供一个可选参数作为你的“上下文”,以达到确保回调函数使用指定this的目的

1
2
3
4
5
function foo(el){
console.log(el, this.id)
}
var obj = {id:'awesome'}
[1, 2, 3].forEach(foo, obj)

new绑定

写在前面

JavaScript中没有构造函数,只有对函数的构造调用
发生函数的构造调用时,自动执行以下操作

  • 创建一个全新的对象
  • 此对象会被执行[[prototype]]链接
  • 此新对象会绑定到函数调用的this
  • 执行此函数代码
  • 若函数无返回值,则自动返回这个新对象
    1
    2
    3
    4
    5
    6
    function fun(){
    this.a = 1
    this.b = 2
    }
    var instance = new fun()
    console.log(instance.a)

箭头函数

箭头函数的this指向就可以理解为传统面向对象语言的this啦。它会根据外层的作用域来决定this,即取决于外层的函数作用域或全局作用域,且箭头函数的绑定无法修改

今天的总结就到这里啦
在这里插入图片描述