Please enable Javascript to view the contents

从头学习js-6-This

 ·  ☕ 5 分钟

这个系列是我读冴羽老师博客的感悟,
加入了个人的解读和练习题的解答

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

举个例子:

1
2
3
4
5
6
7
8
var foo = 1;

//foo对应的Reference是
var fooReference = {
  base: EnvironmentRecoed,
  name: 'foo',
  strict: false
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
var foo = {
  bar: function() {
    return this;
  }
}

foo.bar();

//bar对应的Reference是
var barReference = {
  base: foo,
  propertyName: 'bar',
  strict: false
};

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 //对象创建表达式

例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function foo() {
  console.log(this)
}

foo(); //MemberExpression是foo

function foo() {
  return function () {
    console.log(this)
  }
}

foo()(); //MemberExpression是foo()

var foo = {
  bar: function() {
    return this
  }
}

foo.bar(); //MemberExpression是foo.bar

可以简单判断MemberExpression是()左边的部分

步骤2中判断ref是不是一个Reference类型

例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var value = 1;

var foo = {
  value: 2,
  bar: function() {
    return this.value;
  }
}

//example1
console.log(foo.bar())

//example2
console.log((foo.bar)())

//example3
console.log((foo.bar = foo.bar)())

//example4
console.log((false || foo.bar)())

//example5
console.log((foo.bar, foo.bar)())

example1
MemberExpression为foo.bar属于规范11.2中提到的属性访问,他会返回一个值类型的引用
所以foo.bar会返回一个Reference类型

1
2
3
4
5
var Reference = {
  base: foo,
  name: 'bar',
  strict: false
}

符合确定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


看一个最常见的情况

1
2
3
4
5
6
//example6
function foo() {
  console.log(this)
}

foo()

MemberExpression是foo,是规范10.3.1中提到的标识符解析会返回一个引用对象Reference,

1
2
3
4
5
var fooReference = {
  base: Environment,
  name: 'foo',
  strict: false
}

对照this判断步骤2.2, this的值为 ImplicitThisValue(ref)
在规范10.2.1.1.6中对ImplicitThisValue的解释他始终返回undefined
所以this指向undefined

不管怎么样按照规范去解读this指向从步骤和逻辑上是更为清晰的

分享

Llane00
作者
Llane00
Web Developer