從 ES 規範 中理解 this

從 ES 規範 中理解 this

本覺得對 JS 中的 this 已經很熟練了,再看完冴羽的博客後,才發現本身對 ES 規範知之甚少,原來我都是根據經驗在判斷 this,這篇文章會從最底層的 ES 規範上去介紹 this 的判斷。html

本文已同步到我的博客從 ES 規範 中理解 this,感謝鼓勵。git

一道測試題引起的思考

第一次作這道題時,只對了第一題。。github

var value = 1;

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

//示例1
console.log(foo.bar());
//示例2
console.log((foo.bar)());
//示例3
console.log((foo.bar = foo.bar)());
//示例4
console.log((false || foo.bar)());
//示例5
console.log((foo.bar, foo.bar)());

先想想這道題的答案,而後解釋一下緣由。算法

規範文檔

要完全弄明白上面的測試題,還得看規範文檔 😂ide

常見的幾種規範:函數

  • Reference 類型
  • 函數調用規範
  • 屬性讀取規範
  • 括號運算規範
  • 賦值運算規範
  • 邏輯與算法規範
  • 逗號運算規範

Reference 類型

在 ECMAScript 規範中還有一種只存在於規範中的類型,它們的做用是用來描述語言底層行爲邏輯。測試

  • 規範 8.7 The Reference Specification Typethis

    Reference 類型實例大體長這樣:lua

    var foo = {
      bar: function() {
        return this;
      }
    };
    var fooReference = {
      base: EnvironmentRecord,
      name: 'foo',
      strict: false
    };
    GetBase(fooReference); // EnvironmentRecord;
    
    var barReference = {
      base: 'foo',
      name: 'bar',
      strict: false
    };
    GetBase(barReference); // foo;
    • GetBase(V). Returns the base value component of the reference V.
    • HasPrimitiveBase(V). Returns true if the base value is a Boolean, String, or Number.
    • IsPropertyReference(V). Returns true if either the base value is an object or HasPrimitiveBase(V) is true; otherwise returns false.
  • 8.7.1 GetValue (V)es5

    • If Type(V) is not Reference, return V.
    • Let base be the result of calling GetBase(V).
  • 規範 10.2.1.1.6 ImplicitThisValue()

    • Return undefined.

函數調用規範

  • 規範 11.2.3 Function Calls

    • 步驟1將 ref 賦值爲 MemberExpression(簡單理解 MemberExpression 其實就是()左邊的部分)
    • 步驟2判斷 ref 的類型

      • 步驟3若是 ref 是 Reference 類型

        • 步驟4若是 IsPropertyReference(ref) 是 true, 那麼 this 的值爲 GetBase(ref)
        • 步驟5若是 base value 值是 Environment Record, 那麼 this 的值爲 ImplicitThisValue(ref)
      • 步驟6若是 ref 不是 Reference 類型,那麼 this 的值爲 undefined
提示
非嚴格模式下,this 的值爲 undefined 的時候,其值會被隱式轉換爲全局對象。

示例 1 解答

一、使用屬性讀取規範:獲取 foo.bar 的返回類型。

二、交給函數調用規範,去解析 this。

  • 規範 11.2.1 Property Accessors

    • Return a value of type Reference whose base value is baseValue and whose referenced name is propertyNameString, and whose strict mode flag is strict.
    • 因而可知,屬性讀取,默認返回一個 Reference 類型
  • 函數調用規範

    • 步驟1 -> 步驟2 -> 步驟3 -> 步驟4

示例 2 解答

一、使用屬性讀取規範:獲取 foo.bar 的返回類型。

二、使用括號運算符規範:獲取 (foo.bar) 的返回類型。

三、交給函數調用規範,去解析 this。

  • 查看規範 11.1.6 The Grouping Operator

    • Return the result of evaluating Expression. This may be of type Reference.
    • 實際上 () 並無對 MemberExpression 進行計算,因此其實跟示例 1 的結果是同樣的。
  • 函數調用規範

    • 步驟1 -> 步驟2 -> 步驟3 -> 步驟4

示例 3 解答

一、使用賦值運算符規範:獲取 foo.bar = foo.bar 的返回類型。

二、使用括號運算符規範:獲取 (foo.bar = foo.bar) 的返回類型。

三、交給函數調用規範,去解析 this。

示例 4 解答

一、使用邏輯與算法規範:獲取 false || foo.bar 的返回類型。

二、使用括號運算符規範:獲取 (false || foo.bar) 的返回類型。

三、交給函數調用規範,去解析 this。

  • 規範 11.11 Binary Logical Operators

    • Let rval be GetValue(rref).
    • Return rval. 返回的是 GetValue 後的值,不是一個 Refernce。
  • 函數調用規範

    • 步驟1 -> 步驟2 -> 步驟6

示例 5 解答

一、使用逗號操做符規範:獲取 foo.bar, foo.bar 的返回類型。

二、使用括號運算符規範:獲取 (foo.bar, foo.bar) 的返回類型。

三、交給函數調用規範,去解析 this。

  • 規範 11.14 Comma Operator ( , )

    • Return GetValue(rref). 返回的是 GetValue 後的值,不是一個 Refernce。
  • 函數調用規範

    • 步驟1 -> 步驟2 -> 步驟6

一個最普通的狀況

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

foo();

GetBase(fooReference); // EnvironmentRecord;

一、使用標識符解析規範:獲取 foo 的返回類型。

二、交給函數調用規範,去解析 this。

  • 規範 10.3.1 Identifier Resolution

    • The result of evaluating an identifier is always a value of type Reference with its referenced name component equal to the Identifier String.
  • 函數調用規範

    • 步驟1 -> 步驟2 -> 步驟3 -> 步驟5

總結

遇到問題時,儘可能從原理的角度看待問題,不要憑經驗辦事情,不妨多研究研究底層規範。


tip 參考資料
JavaScript 深刻之從 ECMAScript 規範解讀 this
ES5 規範文檔
相關文章
相關標籤/搜索