这个系列是我读冴羽老师博客的感悟,
加入了个人的解读和练习题的解答
this一直都是学习js的时候很头疼的东西,
继续接着上篇说,当javascript代码执行一段可执行代码(executable code)时,会创建对应的执行上下文(execution context)
那对于每个执行上下文,都有三个重要属性:
- 变量对象(Variable object, VO)
- 作用域链(Scope chain)
- this
本文主要讲执行上下文中的this
首先了解ECMAScript规范
我们根据ECMAScript规范来解读this
ECMAScript 5.1 规范地址:
英文版:http://es5.github.io/#x15.1
中文版:http://yanhaijing.com/es5/#115
javascript 有8种数据类型,Undefined,Null,Boolean,Number,String,Object,Symbol,Bigint
这是我们在编程时可以直接操作使用的数据类型,
在javascript的底层逻辑中还有一些只存在于规范里的抽象类型:
Reference,
List,
Completion,
Property,
Descriptor,
Property Identifier,
Lexical Environment,
Environment Record
这些规范类型是用算法描述ECMAScript语言结构和语言类型的,
其中与this的指向有关的是Reference类型
Reference由三个部分组成:
1.base value (属性所在的对象或者是EnvironmentRecord,所以它的值只能是undefined, an Object, a Boolean, a String, a, Number or an environment record)
2.reference name (属性名称)
3.strict reference
举个例子:
|
|
|
|
Reference涉及到的方法有:
1.GetBase 返回base value
2.isProperty 判断base value是不是一个对象
3.GetVlaue 从Reference处返回一个真正的值(具体的值,不是Reference)
按步骤确定this的指向
规范中确定this的值,有明确的步骤判断:
1.计算MemberExpression的结果赋值给ref
2.判断ref是不是一个Reference类型
2.1 如果ref是Reference,并且base value值是一个对象(isProperty(ref)为true),那么this的值就是base value(用getBase获得)
2.2 如果ref是Reference,并且base value值是Environment Record,那么this的值是ImplicitThisValue(ref)
2.3 如果ref不是Reference,那么this的值是undefined
步骤1中 MemberExpression分别有:
PrimaryExpression //原始表达式
FunctionExpression //函数表达式
MemberExpression [Expression] //属性[]访问表达式
MemberExpression.IdentifierName //属性.访问表达式
new MemberExpression Arguments //对象创建表达式
例子:
|
|
可以简单判断MemberExpression是()左边的部分
步骤2中判断ref是不是一个Reference类型
例子:
|
|
example1
MemberExpression为foo.bar属于规范11.2中提到的属性访问,他会返回一个值类型的引用
所以foo.bar会返回一个Reference类型
|
|
符合确定this的步骤中2.1,IsPropertyReference(ref) 为 true,且base value为foo
所以this指向foo
console.log打印1
example2
(foo.bar)是规范11.1.6中提到的分组表达式,会返回一个Reference类型,
后面的步骤和example1一样所以this指向foo
console.log打印1
example3
(foo.bar = foo.bar)是规范11.13.1中提到的简单赋值会返回一个rval不是一个Reference,
对照this判断步骤的2.3,this的值就是undefined(非严格模式下会隐式转换为全局变量,浏览器中就是window)
console.log打印2
example4
(false || foo.bar)是规范11.11中提到的二元逻辑运算符会返回GetValue(rref)不是一个Reference,
对照this判断步骤2.3,this的值就是undefined
console.log打印2
example5
(foo.bar, foo.bar)是规范11.14中提到的逗号运算符会返回GetValue(rref)不是一个Reference,
对照this判断步骤2.3,this的值是undefined
console.log打印2
看一个最常见的情况
|
|
MemberExpression是foo,是规范10.3.1中提到的标识符解析会返回一个引用对象Reference,
|
|
对照this判断步骤2.2, this的值为 ImplicitThisValue(ref)
在规范10.2.1.1.6中对ImplicitThisValue的解释他始终返回undefined
所以this指向undefined
不管怎么样按照规范去解读this指向从步骤和逻辑上是更为清晰的