根治 JavaScript 中的 this-ECMAScript 规范解读

  GetIdentifierReference

  Return a value of type Reference whose base value is envRec, whose referenced name is name, and whose strict mode flag is strict.

  我们看此处返回了一个 Reference , base value 是 envRec 也就是 10.3.1中传入的execution context’s LexicalEnvironment

  (词法环境为环境记录项Environment Record的组成,它是规范用来管理当前作用域下面变量的类型,此处不用理解,知道它返回了这个东东就行了)

  其抽象数据结构大概为:

  foo_reference = {

  "base": LexicalEnvironment,

  "name": "foo",

  "strict": false}

  至此,第一步执行完毕,ref = foo_reference。

  2. 因为是reference, 执行step 6:

  Type(ref) is Reference

  3. 因为是Environment Record,执行 step6.b:

  Else, the base of ref is an Environment Record.

  4. ImplicitThisValue

  最后,thisValue等于执行ImplicitThisValue的结果,还是查看规范:

  10.2.1.1.6 ImplicitThisValue()

  Declarative Environment Records always return undefined as their ImplicitThisValue.

  到此我们知道,foo()执行时, thisValue = http://www.netofthings.cn/JieJueFangAn/2016-09/undefined; 对应到 JavaScript 代码中的 this,还差最后一步:

  Else if thisArg is null or undefined, set the ThisBinding to the global object.

  真相大白:foo()执行时, this = global = window

  如何解释 foo.bar()

  1 . 执行函数调用规范中的step1:

  Let ref be the result of evaluating MemberExpression.

  foo.bar我们都知道是一个属性访问,那么执行属性访问的时候会发生什么呢? 答案都在规范中 :

  11.2.1 Property Accessors

  The production MemberExpression : MemberExpression [ Expression ] is evaluated as follows:

  1.Let baseReference be the result of evaluating MemberExpression.

  2.Let baseValue be GetValue (baseReference)

  8.Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict

  1) baseReference 等于执行 MemberExpression 在此处为 []左边的语句即 foo 的结果,上一节已经说过了 返回一个 reference .

  2) baseValue 等于 GetValue(baseReference) 。

  8.7.1 GetValue

  * GetValue属于一个规范中比较重要的方法,此处为节约篇幅,暂时不讲,以后可以单开文章讲解。暂时记住他的结果一般为:如果是reference传入,会返回一个普通类型出来。比如 foo 为reference,通过GetValue之后就是一个普通的 object,也就是 foo 对应的 js 类型本身。

  所以,baseValue = http://www.netofthings.cn/JieJueFangAn/2016-09/GetValue(baseReference) = GetValue(reference_foo) = foo

  reference_foo_bar = {

  "base": foo,

  "name": "bar",

  "strict": false}

  2 因为是reference, 执行step 6:

  Type(ref) is Reference

  3. 因为是属性引用类型,执行step 6.a:

  If IsPropertyReference(ref) is true, then

  i.Let thisValue be GetBase(ref).

  GetBase(ref) = reference_foo_bar.base = foo;

  真相大白, foo.bar() 执行时 this 指向 foo

  如何解释 (f = foo.bar)()

  这个语句属于罕见写法,在各种经验总结中属于高级技巧,但是通过规范,一样分分钟搞定。

  1 执行函数调用规范中step1:

  Let ref be the result of evaluating MemberExpression.

  f = foo.bar 是一个明显的赋值语句,我们查看规范,赋值语句会发生什么:

  11.13.1 Simple Assignment ( = )

  The production AssignmentExpression : LeftHandSideExpression = AssignmentExpression is evaluated as follows:

  Let rref be the result of evaluating AssignmentExpression.

  Let rval be GetValue (rref)

  Return rval.

  可以看出简单赋值语句返回的是对等于号右边进行GetValue之后的结果,上一节讲了,foo.bar是一个属性访问的reference类型,而执行过GetValue之后的reference就会返回普通类型。

  所以,rval = GetValue(rref) = GetValue(reference_foo_bar) = foo

  2 ref不是reference,执行step7:

  7.Else, Type(ref) is not Reference.

  3 thisValue = http://www.netofthings.cn/JieJueFangAn/2016-09/undefined

  同理:

  Else if thisArg is null or undefined, set the ThisBinding to the global object.

  真相大白, (f = foo.bar)() 执行时 this = global = window

  老司机的经验

  虽然我们不是江湖郎中,但是每次查询规范也有点麻烦,此处还是有一定规律可循的。