this何时存在?
由[深入理解js - AzirKxs的博客 | 分享与记录](http://8.130.40.222/cn/base05- 深入理解js/)我们可以知道,this是和执行上下文绑定的,也就是说每个执行上文中都有自己对应的this,执行上下文主要分为全局执行上下文,函数执行上下文和eval,eval几乎不用,暂时不做区分。也就是我们所需要探讨的核心就是全局执行上下文中的this和函数执行上下文中的this。
全局执行上下文
废话不多说,直接打印
1 | console.log(this); //window |
就是这么简单,全局执行上下文中的this就是window
函数执行上下文
我们先做一个简单的推测,既然this是在执行上下文中绑定的,而执行上下文是在代码执行时才有的,那么this的值一定与函数的执行有关!(好像说了句废话…),那么我们该如何确定一个this的值呢?
ECMAScript规范
emmm……我也想从规范的角度去理解this,但是看了两篇规范相关的文章了,还是有点迷糊,总之this的值是有一定的规则的,它是由调用方式和所处环境所决定的
ECMAScript2016规范理解(1)-this | 李冬琳的博客 (ldllidonglin.github.io)
JavaScript深入之从ECMAScript规范解读this · Issue #7 · mqyqingfeng/Blog (github.com)
接下来我简单在做一个介绍,注意一下的内容都只在规范中存在:
确定this值的过程:
1.计算 MemberExpression 的结果赋值给 ref
2.判断 ref 是不是一个 Reference 类型
3.根据规则确定this的值
1
2
3
4
5 如果 ref 是 Reference,并且 IsPropertyReference(ref) 是 true, 那么 this 的值为 GetBase(ref)
如果 ref 是 Reference,并且 base value 值是 Environment Record, 那么this的值为 ImplicitThisValue(ref)
如果 ref 不是 Reference,那么 this 的值为 undefined
MemberExpression
简单的说,MemberExpession的值就是函数调用时()左边的部分
1 | function foo() { |
Reference
Reference由以下三个部分组成
- base value
- referenced name
- strict reference
base value 就是属性所在的对象或者就是 EnvironmentRecord,它的值只可能是 undefined, an Object, a Boolean, a String, a Number, or an environment record 其中的一种。
1 | var foo = 1; |
1 | var foo = { |
此外,reference还有两个相关的方法
1.GetBase
获取base value
2.IsPropertyReference
判断base value是否是一个对象
GetValue
GetValue是一个从 Reference 类型获取对应值的方法,调用 GetValue,返回的将是具体的值,而不再是一个 Reference,如果使用了+,||等运算将会调用GetValue方法导致MemberExpression不再是一个reference
根据规则规范判断this
案例1
看下面的例子
1 | var value = 1; |
- 首先判断MemberExpression
根据上面的规则我们可以得知MemberExpression为foo.bar
- 判断foo.bar是否是一个Reference
foo.bar是一个reference
1 | barReference ={ |
- 判断base value的类型
base value是一个对象
- 确定this的值
根据规则2.1 this = getBase(foo.bar)也就是bar的base value值为foo
案例2
1 | var value = 1; |
- 首先判断MemberExpression
根据上面的规则我们可以得知MemberExpression为foo.bar
- 判断foo.bar = foo.bar是否是一个Reference
因为使用了=运算,调用了GetValue方法,得到的不再是一个reference类型,因此不是一个reference类型
- 判断this
根据规则3,this为undefined
案例3
1 |
|
- 首先判断MemberExpression
根据上面的规则我们可以得知MemberExpression为foo
- 判断foo是否是一个Reference
foo是一个reference
1 | fooReference ={ |
- 判断base类型
base为environmentRecord,遵循规则2
- 判断this
根据规范,ImplicitThisValue(ref)的值为undefined
注意案例3只有在严格模式下才为undefined,一把模式下为window
this的值
了解了规范,我们可以做一些简单易懂的总结了,this 永远指向最后调用它的那个对象
先来看案例:
1 |
|
1 |
|
不管怎么花式赋值,this的值永远与它最后的调用方式有关!
绑定规则
上面我们从函数的角度判断了this的值,其中只包括了我们没有对this值进行更改的情况(默认绑定,隐式绑定),接下来我们从绑定方式的角度进行一个更详细深入探究。
默认绑定
1 | function foo(){ |
隐式绑定
1 | function foo(){ |
1 | function foo() { |
显式绑定
◼ 隐式绑定有一个前提条件:
必须在调用的对象内部有一个对函数的引用(比如一个属性);
如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
正是通过这个引用,间接的将this绑定到了这个对象上;
如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,就要用到apply和call方法了
非严格模式下,如果显示绑定到null或者undefined,则忽略该显示绑定;严格模式下,this为null或者undefined
call
call方法可以将this绑定到call后面的指定的参数上
1 | function foo() { |
1 |
|
call方法也可以传递参数,需要将传递的参数根在需要绑定的参数后面
1 | function foo(id,name,age){ |
apply
apply与call方法的作用一模一样,区别在于call方法传递参数时需要以数组的形式传递
1 | function foo(id,name,age){ |
bind
如果我们需要将函数一直绑定在某个参数上,不想每次都调用call/applay方法,就可以使用bind方法
1 | function foo(id,name,age){ |
new绑定
使用new关键字来调用函数的时候,会执行如下的操作:
1.创建一个全新的对象;
2.这个新对象会被执行prototype连接;
3.这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
4.如果函数没有返回其他对象,表达式会返回这个新对象;
1 | function foo(id,name,age){ |
绑定优先级
既然有这么多绑定规则,如果我同时使用了,那么该听谁的呢?以下做一个总结
1.默认规则的优先级最低
2.显示绑定优先级高于隐式绑定
3.new绑定优先级高于隐式绑定
4.new绑定优先级高于bind
-
new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
-
new绑定可以和bind一起使用,new绑定优先级更高
箭头函数
在es6中推出了箭头函数,箭头函数不会绑定this,arguments属性且箭头函数不能作为构造函数来使用
箭头函数并不绑定this对象,this引用就会从上层作用于中找到对应的this
看下面的例子来区分箭头函数与普通函数的this
1 | var obj = { |
以上就是关于this的相关总结了!
如果这篇文章对你有帮助,可以bilibili关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!