super
是一個 javascript 得關鍵字。也正是因爲它是一個關鍵字,使得標準有機會爲它得每一種用法單獨定義行爲,從而使它得用法不同凡響。ECMA262 有專門的一節 The super keyword 來介紹 super 的用法。javascript
在派生類(class Derived extends Base
)的構造裏,能夠經過 super(...)
的方式調用基類的構造函數。這個稱做 superCall。在調用 superCall 以前,this
綁定並未初始化,不能使用 this
。superCall 會使用基類構造函數的「返回值」初始化當前的 this
。(這裏的「返回值」,相似於 new Base(...)
的結果)java
在派生類的構造,或者成員函數中,可使用 super[prop]
或者 super.property
形式,稱做 superProperty 。算法
superProperty 的行爲跟其餘的 A.B
的形式的結構表現很對相同。主要的 super.property
,super
不是一個對象,這不是一個取對象屬性的表達。標準直接定義了 super.property
做爲一個總體的含義。async
在繼續說 superProperty 以前,先要介紹一下 Reference ,由於 A.B
或 superProperty 的結果都是一個 Reference 。Reference 是一種標準內置類型。它用來表示標識符解析的結果,也就是說,在什麼地方找到了某一個標識符。在須要取值的時候或賦值的時候,會經過 Reference 進行。函數
下面主要介紹 Reference
中結構中與,與 A.B
與 superProperty 相關的部分。this
A.B
、superProperty 生成的 Reference 通常記錄瞭如下幾個信息:lua
base value: 這個標識符是在哪裏找到的。它能夠一個 Object, 基本類型的值,或者是一個環境(Environment Record),或者是 undefined
prototype
A.B
中,爲 A
。即將會從 A
中查找屬性 "B"
。(注意此時不會檢查對象中是否真的存在這個屬性)super.B
中,base value 爲 Object.getPrototypeOf([[HomeObject]])([[HomeObject]]的原型對象。[[HomeObject]]是啥一會介紹)referenced name: 這是一個字符串。表示標識符的名字。code
A.B
中,爲 "B"
super.B
獲得的 Reference 中存在,爲 super.B
所在環境的 this
。[[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.prototype
;static
方法的 [[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
。
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):
- ReturnIfAbrupt(V).
- If Type(V) is not Reference, return V.
- Let base be GetBase(V).
- If IsUnresolvableReference(V) is
true
, throw aReferenceError
exception.If IsPropertyReference(V) is
true
, then
If HasPrimitiveBase(V) is
true
, then
- Assert: In this case, base will never be
undefined
ornull
.- Set base to ! ToObject(base).
- Return ? base.[[Get]](GetReferencedName(V), GetThisValue(V)).
Else base must be an Environment Record,
- 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]] 是 javascript 對象的一個內置槽,不一樣的對象會不太相同。對普通的對象來講,它是以下定義的:
- Return ? OrdinaryGet(O, P, Receiver).
- Assert: IsPropertyKey(P) is
true
.- Let desc be ? O.[[GetOwnProperty]](P).
If desc is
undefined
, then
- Let parent be ? O.[[GetPrototypeOf]]().
- If parent is
null
, returnundefined
.- Return ? parent.[[Get]](P, Receiver).
- If IsDataDescriptor(desc) is
true
, return desc.[[Value]].- Assert: IsAccessorDescriptor(desc) is
true
.- Let getter be desc.[[Get]].
- If getter is
undefined
, returnundefined
.- Return ? Call(getter, Receiver).
對比 GetValue ,能夠看到,OrdinaryGet 中的 O 與 Receiver ,對普通對象屬性,均爲 base value ;對 superProperty ,分別是 base value 與 thisValue 。
這裏僅有第 8 步用到了 Receiver 。也就是說,若是這個屬性是一個 getter ,那麼 getter 函數中的 this
對 superProperty 來講將是 thisValue ,而在普通對象屬性中,將是 base value 。
也就是說,在 superProperty 中, base value 提供了屬性的定義,thisValue 提供了具體實現。這個定義若是是一個值,那麼就直接返回了。這個定義若是是一個 getter ,那麼這個函數在 thisValue (具體實現)上執行。
在普通屬性取值中,在通過 OrdinaryGet 第三步順着原型鏈向上走,並遞歸調用到 OrdianryGet 時,O 與 Receiver 也再也不相同。O 此時是原型對象,Receiver 依然是原始取屬性的對象。這一樣能夠理解爲原型對象提供了屬性定義,而實際對象提供的屬性的實現。
向 Reference V 賦值 W ,是經過 PutValue(V, W) 實現的:
- ReturnIfAbrupt(V).
- ReturnIfAbrupt(W).
- If Type(V) is not Reference, throw a
ReferenceError
exception.- Let base be GetBase(V).
If IsUnresolvableReference(V) is
true
, then
If IsStrictReference(V) is
true
, then
- Throw a
ReferenceError
exception.- Let globalObj be GetGlobalObject().
- Return ? Set(globalObj, GetReferencedName(V), W,
false
).Else if IsPropertyReference(V) is
true
, then
If HasPrimitiveBase(V) is
true
, then1. Assert: In this case, *base* will never be `undefined` or `null`. 2. Set *base* to ! ToObject(*base*).- Let succeeded be ? base.[[Set]](GetReferencedName(V), W, GetThisValue(V)).
- If succeeded is
false
and IsStrictReference(V) istrue
, throw aTypeError
exception.- Return.
Else base must be an Environment Record,
- Return ? base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)) (see 8.1.1).
與本文有關的是第 6 步,它使用了 base value 的 [[Set]] 方法來設置屬性值。
對普通對象來講,它是以下定義的:
- Return ? OrdinarySet(O, P, V, Receiver).
OrdinarySet(O, P, V, Receiver) :
- Assert: IsPropertyKey(P) is true.
- Let ownDesc be ? O.[[GetOwnProperty]](P).
- Return OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).
OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc) :
- Assert: IsPropertyKey(P) is true.
If ownDesc is
undefined
, then
- Let parent be ? O.[[GetPrototypeOf]]().
If parent is not
null
, then
- Return ? parent.[[Set]](P, V, Receiver).
Else,
- Set ownDesc to the PropertyDescriptor { [[Value]]:
undefined
, [[Writable]]:true
, [[Enumerable]]:true
, [[Configurable]]:true
}.If IsDataDescriptor(ownDesc) is
true
, then
- If ownDesc.[[Writable]] is
false
, returnfalse
.- If Type(Receiver) is not Object, return
false
.- Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P).
If existingDescriptor is not
undefined
, then
- If IsAccessorDescriptor(existingDescriptor) is
true
, returnfalse
.- If existingDescriptor.[[Writable]] is
false
, returnfalse
.- Let valueDesc be the PropertyDescriptor { [[Value]]: V }.
- Return ? Receiver.[[DefineOwnProperty]](P, valueDesc).
Else Receiver does not currently have a property P,
- Return ? CreateDataProperty(Receiver, P, V).
- Assert: IsAccessorDescriptor(ownDesc) is true.
- Let setter be ownDesc.[[Set]].
- If setter is
undefined
, returnfalse
.- Perform ? Call(setter, Receiver, « V »).
- 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 。對普通屬性來講,O 與 Receiver 原本時一致的。可是,通過 2.2.1 遞歸進入原型鏈以後,O 將爲原型對象,但 Receiver 依然爲原調用對象。
當 Reference V 的值是一個函數,直接調用這個函數時,函數中的 this
綁定爲 GetThisValue(V) (參見 EvaluateCall)。對 superProperty 來講,getThisValue(V) 的結果就是 SuperProperty 所在環境的 this
。
對於 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
。
取值的時候,只有定義在基類/原型對象中的屬性能夠被讀取到。定義在派生類/類實例中的屬性是不可見的。(會返回 undefined
)
this
來調用 getter 。賦值的時候,值實際會被寫入 this
。
undefined
。this
來調用 setter 。this
綁定,爲 superProperty 所處環境的 this
。