簡單易懂的ECMA規範導讀1 that's this

簡單易懂的ECMA規範導讀1

最近混SF,恰巧又逢工做方面有了NodeJS的機會,迫切地有教別人怎麼寫JS的需求,
我發現JS這個東西其實真沒那麼容易理解。segmentfault

爲了加深和糾正本身對JS的理解,也爲了之後能直接甩別人一臉文章,因此開始挖這樣一個大坑:簡單易懂的ECMA規範導讀
但願能以專題的形式有線索地基於ECMA標準介紹Javascript的方方面面。本文不是ECMA標準的中文翻譯,也不是Javascript的入門教程,
本文雖然以JS的常見問題切入,但並不適合想要快速瞭解這些問題的人(Google纔是快速瞭解問題的正解)。
本文的聚焦於標準如何決定了JS的各類行爲,JS引擎的水面下在發生些什麼。瀏覽器

本文描述的是ECMA262的5.1版本 也是如今最爲流行和主流的標準,
現代瀏覽器和NodeJS默認均遵循此標準。儘可能以英文原版爲基礎,爲了流暢,可能會使用某些名詞的中文翻譯,
但會將匹配的英文名詞以此種樣式中出現一次以免誤解。app

Topic1. that's this

咱們的第一個話題是:this指向哪裏?函數

什麼是this

11.1.1 The this Keywordthis

The this keyword evaluates to the value of the ThisBinding of the current execution context.lua

計算this關鍵字時,取當前執行上下文的ThisBinding的值翻譯

10.3 Execution Contextscode

執行上下文Execution Context 從邏輯上造成棧結構,棧頂(活躍)的執行上下文包含了追蹤當前正在執行的代碼的所有狀態。orm

執行上下文包含了LexicalEnvironment、VariableEnvironment和ThisBinding三部分,在這個話題中咱們主要關心ThisBinding,
也就是代碼中出現this所代指的值的綁定對象

全局代碼中的this

從最簡單的開始

10.4.1 Entering Global Code
在進入全局代碼的流程中,規範明確指出:全局代碼對應的執行上下文中,

Set the ThisBinding to the global object.

因此全局代碼中,this指向全局對象

函數調用表達式時提供的this值

注意:this值this value是不一樣於this關鍵詞的概念,是調用[[Call]]內部方法的參數之一,並不等同於用戶代碼中的this關鍵字

函數調用表達式CallExpression的過程當中,按照標準的描述
計算this值的僞代碼以下

if Type(ref) is <Reference>
    if IsPropertyReference(ref)
        thisValue := getBase(ref)
    else # assert Type(getBase(ref)) is <Environment Record>
        thisValue := getBase(ref).ImplicitThisValue()
else
    thisValue := undefined
  • ref是函數調用參數左側(括號左側)的表達式計算的結果
  • <Reference>引用類型常見的有

  • 非引用類型常見的有

    • 所有ECMA內置函數和全部用戶定義函數的返回結果(例外是host objects,也就是假設DOM之類的宿主對象若是須要,
      能夠定義一些函數返回引用)
  • 環境記錄是前述的執行上下文中的LexicalEnvironment和VariableEnvironment的構成要素

綜上所述,排除with語句的狀況下,想讓thisValue不是undefined,就只有屬性訪問一種辦法而已。

計算獲得thisValue後,調用被調函數func的[[call]]內部方法,提供thisValue做爲this的值

new表達式時提供的this值

new表達式NewExpression執行過程
基本上委託給了[[Construct]]內部方法,咱們看這個方法的定義
關注其中第8步

Let result be the result of calling the [[Call]] internal property of F, providing
obj as the this value and providing the argument list passed into [[Construct]] as args.

其中F是構造函數,而obj是本次新建的對象,很是清楚。

this值如何轉變爲ThisBinding

前兩節咱們描述了兩種觸發函數體內代碼的辦法各自如何構造this值,但正如前述,this關鍵字的值是執行上下文中的ThisBinding決定的,
this值轉變爲ThisBinding的過程發生在調用[[Call]]內部方法時,咱們看標準

Let funcCtx be the result of establishing a new execution context for function
code using the value of F's [[FormalParameters]] internal property, the passed
arguments List args, and the this value as described in 10.4.3.

阿哈,第一步就是創建新的執行上下文,其中thisBinding的構建在10.4.3 Entering Function Code
中描述,

if function-code is <strict code>
    ThisBinding = thisArg
else if thisArg is null or thisArg is undefined
    ThisBinding = global object
else if Type(thisArg) is not <Object>
    ThisBinding = ToObject(thisArg)
else
    ThisBinding = thisArg

這就是魔術的祕密

  • null 和 undefined 的this在此時會綁定爲全局對象
  • 其餘三種非對象 String / Boolean / Number 在此時被轉化爲對象(auto boxing)
  • 可是,strict mode下不進行任何轉換

That's this in Javascript.


本節思考題:

  • 找找看關於apply和call的標準,爲什麼this會變?
  • 找找看關於bind的標準,哪裏體現了bind後的函數內的this無視環境和調用方式,老是固定值?
  • with語句並非一個良好的實踐,因此我避開了它,不過這能夠做爲閱讀標準的練習:with語句如何影響this關鍵詞?請試着寫一句代碼展現此種影響
  • eval的內部的this如何肯定?
相關文章
相關標籤/搜索