super

super

super 是一個 javascript 得關鍵字。也正是因爲它是一個關鍵字,使得標準有機會爲它得每一種用法單獨定義行爲,從而使它得用法不同凡響。ECMA262 有專門的一節 The super keyword 來介紹 super 的用法。javascript

superCall

在派生類(class Derived extends Base)的構造裏,能夠經過 super(...) 的方式調用基類的構造函數。這個稱做 superCall。在調用 superCall 以前,this 綁定並未初始化,不能使用 this 。superCall 會使用基類構造函數的「返回值」初始化當前的 this 。(這裏的「返回值」,相似於 new Base(...) 的結果)java

superProperty

在派生類的構造,或者成員函數中,可使用 super[prop] 或者 super.property 形式,稱做 superProperty算法

superProperty 的行爲跟其餘的 A.B 的形式的結構表現很對相同。主要的 super.propertysuper 不是一個對象,這不是一個取對象屬性的表達。標準直接定義了 super.property 做爲一個總體的含義。async

Reference

在繼續說 superProperty 以前,先要介紹一下 Reference ,由於 A.B 或 superProperty 的結果都是一個 Reference 。Reference 是一種標準內置類型。它用來表示標識符解析的結果,也就是說,在什麼地方找到了某一個標識符。在須要取值的時候或賦值的時候,會經過 Reference 進行。函數

下面主要介紹 Reference 中結構中與,與 A.B 與 superProperty 相關的部分。this

A.B 、superProperty 生成的 Reference 通常記錄瞭如下幾個信息:lua

  1. base value: 這個標識符是在哪裏找到的。它能夠一個 Object, 基本類型的值,或者是一個環境(Environment Record),或者是 undefinedprototype

    • A.B 中,爲 A。即將會從 A 中查找屬性 "B"。(注意此時不會檢查對象中是否真的存在這個屬性)
    • super.B 中,base value 爲 Object.getPrototypeOf([[HomeObject]])([[HomeObject]]的原型對象。[[HomeObject]]是啥一會介紹)
  2. referenced name: 這是一個字符串。表示標識符的名字。code

    • A.B 中,爲 "B"
  3. strict: 引用標識符的地點是否處於嚴格模式
  4. thisValue: 僅在 super.B 獲得的 Reference 中存在,爲 super.B 所在環境的 this

[[HomeObject]]

[[HomeObject]] 是專門爲 superProperty 存在的。Object.getPrototypeOF([[HomeObject]]) 將成爲 superProperty 結果 Reference 的 base value。orm

只有成員函數(包括 getter、setter)、contructor 纔有 [[HomeObject]] ,其它函數沒有 [[HomeObject]] 。除箭頭函數外,只有在有 [[HomeObject]] 的函數中,纔可使用 superProperty。對箭頭函數來講,若是箭頭函數定義所在的最內層非箭頭函數有 [[HomeObject]] ,那麼在箭頭函數中也可使用 superProperty ,他們共享同一個 [[HomeObject]]。(也就是說,直接定義在類成員方法中的箭頭函數中可使用 superProperty。)

class 的定義是經過 ClassDefinitionEvaluation 算法處理的。它爲 class 的每個成員函數(包括 getter 、setter)定義了 [[HomeObject]] 。

對於 class Derived extends Base ,非 static 方法的 [[HomeObject]] 是 Derived.prototypestatic 方法的 [[HomeObject]] 是 Derived

根據類的定義,Object.getPrototypeOf(Derived.prototype) === Base.protorype 。當 extends Base 不存在時(基類定義),Object.getPrototypeOf(Derived.prototype) === Object.prototype。也就是說,非 static 方法的 [[HomeObject]] 時當前類的原型對象,superProperty 的 base value 時其基類的原型對象。

Derived 自己時一個構造函數,根據類的定義,Object.getPrototypeOf(Derived) === Base 。當 extends Base 不存在時,Object.getPrototypeOf(Derived) == Function.prototype 。對於 static 方法,[[HomeObject]] 是當前類的構造函數,superProperty 的 base value 是基類的構造函數;沒有基類時,是 Function.prototype

object literal

javascript 中的 Object literal 中以 MethodDefinition 形式直接定義的方法,也有 [[HomeObject]] 的定義。以下例中,method 中就有 [[HomeObject]] 定義。可是, non_method 對應的 function 並非直接定義的,而是做爲 non_method 屬性的值,於是並無 [[HomeObject]] 定義。

{
    method(){}
    non_method : function(){}
}

MethodDefinition 的形式包括:

{
    method(...){...}
    *generatorMethod(...){...}
    async asyncMethod(...){...}
    async *asyncGeneratorMethod(...){...}
    get propertyName(){...}
    set propertyName(...){...}
}

在 object literal 的構建算法,爲成員方法指定了 [[HomeObject]] 。這個 [[HomeObject]] 就是這個 object literal 自己。其原型對象(也就是 superProperty 的 base value)是 Object.prototype

取值

Reference 只記錄了應該從何處去找到一個對象,並無記錄對象的值。取值的時候,須要到這個地方把只拿出來。這個操做,在 ECMA262 中稱爲 GetValue(V) (其中 V 是須要被取值的 Reference):

  1. ReturnIfAbrupt(V).
  2. If Type(V) is not Reference, return V.
  3. Let base be GetBase(V).
  4. If IsUnresolvableReference(V) is true, throw a ReferenceError exception.
  5. If IsPropertyReference(V) is true, then

    1. If HasPrimitiveBase(V) is true, then

      1. Assert: In this case, base will never be undefined or null.
      2. Set base to ! ToObject(base).
    2. Return ? base.[[Get]](GetReferencedName(V), GetThisValue(V)).
  6. Else base must be an Environment Record,

    1. Return ? base.GetBindingValue(GetReferencedName(V), IsStrictReference(V)) (see 8.1.1).

與本文相關的是第 5 步,最終取值的結果是 base.[[Get]](GetReferencedName(V), GetThisValue(V)) 。

這個 base 就是 base value 。[[Get]] 是取對象屬性得內置方法。GetReferencedName(V) 返回 V 中記錄得屬性名 (referenced name)。GetThisValue(V) 返回 V 中記錄得 thisValue (superProperty);若是 thisValue 不存在(不是 superProperty),GetThisValue(V) 將返回 V 中得 baseValue 。

這裏,superProperty 與普通的取對象屬性並不相同,區別就在於 [[Get]] 的第二個參數。

[[Get]]

[[Get]] 是 javascript 對象的一個內置槽,不一樣的對象會不太相同。對普通的對象來講,它是以下定義的:

O.[[Get]](P, Receiver)

  1. Return ? OrdinaryGet(O, P, Receiver).

OrdinaryGet(O, P, Receiver)

  1. Assert: IsPropertyKey(P) is true.
  2. Let desc be ? O.[[GetOwnProperty]](P).
  3. If desc is undefined, then

    1. Let parent be ? O.[[GetPrototypeOf]]().
    2. If parent is null, return undefined.
    3. Return ? parent.[[Get]](P, Receiver).
  4. If IsDataDescriptor(desc) is true, return desc.[[Value]].
  5. Assert: IsAccessorDescriptor(desc) is true.
  6. Let getter be desc.[[Get]].
  7. If getter is undefined, return undefined.
  8. Return ? Call(getter, Receiver).

對比 GetValue ,能夠看到,OrdinaryGet 中的 OReceiver ,對普通對象屬性,均爲 base value ;對 superProperty ,分別是 base value 與 thisValue 。

這裏僅有第 8 步用到了 Receiver 。也就是說,若是這個屬性是一個 getter ,那麼 getter 函數中的 this 對 superProperty 來講將是 thisValue ,而在普通對象屬性中,將是 base value 。

也就是說,在 superProperty 中, base value 提供了屬性的定義,thisValue 提供了具體實現。這個定義若是是一個值,那麼就直接返回了。這個定義若是是一個 getter ,那麼這個函數在 thisValue (具體實現)上執行。

在普通屬性取值中,在通過 OrdinaryGet 第三步順着原型鏈向上走,並遞歸調用到 OrdianryGet 時,OReceiver 也再也不相同。O 此時是原型對象,Receiver 依然是原始取屬性的對象。這一樣能夠理解爲原型對象提供了屬性定義,而實際對象提供的屬性的實現。

賦值

向 Reference V 賦值 W ,是經過 PutValue(V, W) 實現的:

  1. ReturnIfAbrupt(V).
  2. ReturnIfAbrupt(W).
  3. If Type(V) is not Reference, throw a ReferenceError exception.
  4. Let base be GetBase(V).
  5. If IsUnresolvableReference(V) is true, then

    1. If IsStrictReference(V) is true, then

      1. Throw a ReferenceError exception.
    2. Let globalObj be GetGlobalObject().
    3. Return ? Set(globalObj, GetReferencedName(V), W, false).
  6. Else if IsPropertyReference(V) is true, then

    1. If HasPrimitiveBase(V) is true, then

      1. Assert: In this case, *base* will never be `undefined` or `null`.
      2. Set *base* to ! ToObject(*base*).
    2. Let succeeded be ? base.[[Set]](GetReferencedName(V), W, GetThisValue(V)).
    3. If succeeded is false and IsStrictReference(V) is true, throw a TypeError exception.
    4. Return.
  7. Else base must be an Environment Record,

    1. Return ? base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)) (see 8.1.1).

與本文有關的是第 6 步,它使用了 base value 的 [[Set]] 方法來設置屬性值。

對普通對象來講,它是以下定義的:

O.[[Set]](P, V, Receiver)

  1. Return ? OrdinarySet(O, P, V, Receiver).

OrdinarySet(O, P, V, Receiver)

  1. Assert: IsPropertyKey(P) is true.
  2. Let ownDesc be ? O.[[GetOwnProperty]](P).
  3. Return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).

OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc)

  1. Assert: IsPropertyKey(P) is true.
  2. If ownDesc is undefined, then

    1. Let parent be ? O.[[GetPrototypeOf]]().
    2. If parent is not null, then

      1. Return ? parent.[[Set]](P, V, Receiver).
    3. Else,

      1. Set ownDesc to the PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
  3. If IsDataDescriptor(ownDesc) is true, then

    1. If ownDesc.[[Writable]] is false, return false.
    2. If Type(Receiver) is not Object, return false.
    3. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
    4. If existingDescriptor is not undefined, then

      1. If IsAccessorDescriptor(existingDescriptor) is true, return false.
      2. If existingDescriptor.[[Writable]] is false, return false.
      3. Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
      4. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
    5. Else Receiver does not currently have a property P,

      1. Return ? CreateDataProperty(Receiver, P, V).
  4. Assert: IsAccessorDescriptor(ownDesc) is true.
  5. Let setter be ownDesc.[[Set]].
  6. If setter is undefined, return false.
  7. Perform ? Call(setter, Receiver, « V »).
  8. Return true.

這,能夠看到,O 依然只是提供了屬性的定義。若是 O 中的屬性不可寫,哪一個賦值將失敗(3.1)。可是,實際寫入時發生了 Receiver (實現)中的(3.4.4;3.5.1)。若是實現(Receiver)中已經存在了這個屬性,那麼 Receiver 中的屬性也要可寫,而且類型一致(4.1.1;4.1.2)。若是 O 的定義是一個 setter ,那麼已 Receiver (實現)爲 this 調用 setter (7)。

對 superPreperty ,O 最初爲 base value ,Receiver 爲 thisValue 。對普通屬性來講,OReceiver 原本時一致的。可是,通過 2.2.1 遞歸進入原型鏈以後,O 將爲原型對象,但 Receiver 依然爲原調用對象。

函數調用

當 Reference V 的值是一個函數,直接調用這個函數時,函數中的 this 綁定爲 GetThisValue(V) (參見 EvaluateCall)。對 superProperty 來講,getThisValue(V) 的結果就是 SuperProperty 所在環境的 this

base value 與 thisValue

對於 superProperty ,base value 提供了屬性定義,thisValue 爲實現。一般狀況下(不去故意改變 this 綁定時),base value 會處於 thisValue 的原型鏈中。

對於 class Derived extends Base 的非 static 成員方法,this 一般爲 new Derived 獲得的對象,[[HomeObject]] 爲 Derived.prototype ,base value 爲 Object.getPrototypeOf([[HomeObject]]) === Object.getPrototypeOf(Dereived.prototype) === Base.prototype。而 Object.getPrototypeOf(this) === Dreived.prototype。即 Object.getPrototypeOf(Object.getPrototypeOf(thisValue)) === base value ;base value 是 thisValue 的原型對象的原型對象。

static 成員方法,this 一般爲 Derived ;[[HomeObject]] 也時 Derived 。base value 時 [[HomeObject]] 的原型對象(Base),也就是 thisValue 的原型對象。

對 object literal 內直接定義的方式,this 於 [[HomeObject]] 一般也是一致的,爲 object 自己。從而,base value 爲 thisValue 的原型對象。

this 綁定是在函數調時決定的。可是 base value ([[HomeObject]])時在函數定義時就已經肯定的。因此,若是在函數調用時經過各類途徑,改變了 this 一般的取值,那麼上述關係將再也不成立。

小結

javascipt 中對對象屬性的讀寫,都要經過原型鏈先找到屬性的定義,而後根據定義在實際對象中讀寫。對普通對象屬性來講,查找的起點與被讀寫的對象是同一個對象。

對於 superProperty ,定義的查找是從基類的原型對象(類成員非 static 方法),或者 this 的原型對象(object literal,類 static 成員方法)開始的。而實際讀寫的對象都是 this 。因爲查找是從原型對象開始的,因此「派生類」的屬性是找不到的。派生類的實例中的屬性也是找不到的。

能夠認爲,superProperty 只是改變的屬性定義查找的起點,從而只有基類中的屬性才能夠被找到,但實際操做的對象依然爲 this

  1. 取值的時候,只有定義在基類/原型對象中的屬性能夠被讀取到。定義在派生類/類實例中的屬性是不可見的。(會返回 undefined

    • 若是定義爲 getter ,會以當前的 this 來調用 getter 。
  2. 賦值的時候,值實際會被寫入 this

    • 從而,對 superProperty 賦值以後再讀取,其結果依然多是 undefined
    • 若是定義爲 setter ,會以當前的 this 來調用 setter 。
  3. 直接函數調用,函數中的 this 綁定,爲 superProperty 所處環境的 this
相關文章
相關標籤/搜索