背景
看到繼承與原型鏈就會想到prototype
與__proto__
,作為一個菜鳥,在昨天之前只用過prototype
,知道有一個叫__proto__
的東西,但是不知道是做什麼的。
但是昨天遇到一個vue的繼承問題,通過instanceof
判斷不出來變數是不是通過Vue.extend
生成子類,程式碼如下:
let component = Vue.extend({
mounted(){
console.log('mounted')
}
})
console.log(component instanceof Vue) // false
複製程式碼
vuejs官方文件是這麼介紹vue.extend
這個方法的
既然是"子類"為什麼instanceof的結果是false。
帶著這個問題,開始深入學習一下js的繼承與原型鏈。
學習
說到繼承與原型鏈,不得不先放張圖鎮樓
這張圖看不懂沒關係,一步一步慢慢來。
先看下面程式碼
// 宣告一個類
function Test(){
this.a = 1
this.b = 2
}
// 例項化
let ins = new Test()
// 原型鏈上追加屬性
Test.prototype.b = 3
Test.prototype.c = 4
console.log(ins.a) // 1
// 這裡的a是例項上的屬性
console.log(ins.b) // 2
// 這裡的b是例項上的屬性
console.log(ins.c) // 4
// 這裡的c是原型鏈上的屬性
複製程式碼
依次列印這幾個變數,會有以下這樣的結果
-> console.log(ins)
{
"a": 1,
"b": 2,
"__proto__": {
"b": 3,
"c": 4,
"__proto__": {
"hasOwnProperty": function
}
}
}
複製程式碼
-> console.log(Test)
{
"arguments": null,
"caller": null,
"length": 0,
"name": "Test",
"prototype": {
"b": 3,
"c": 4,
"__proto__": {
"hasOwnProperty": function
}
}
}
複製程式碼
console.log(ins.__proto__ === Test.prototype) // true
複製程式碼
這就很明確了,ins.__proto__
指向了Test.prototype
。通過檢視MDN,通過new生成一個例項的過程是這樣的:
// js程式碼
var o = new Foo();
// js實際執行程式碼
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
複製程式碼
這段程式碼說明讓我產生了極大的興趣,下面是梳理ecma官方標準
當執行new操作的時候會執行以下步驟:
- Assert: constructExpr is either a NewExpression or a MemberExpression.
- Assert: arguments is either empty or an Arguments.
- Let ref be the result of evaluating constructExpr.
- Let constructor be ? GetValue(ref).
- If arguments is empty, let argList be a new empty List.
- Else,
- Let argList be ArgumentListEvaluation of arguments.
- ReturnIfAbrupt(argList).
- If IsConstructor(constructor) is false, throw a TypeError exception.
- Return ? Construct(constructor, argList).
最後一步的Construct是這麼定義的: Construct ( F [ , argumentsList [ , newTarget ]] )
- If newTarget is not present, set newTarget to F.
- If argumentsList is not present, set argumentsList to a new empty List.
- Assert: IsConstructor(F) is true.
- Assert: IsConstructor(newTarget) is true.
- Return ? F.[[Construct]](argumentsList, newTarget).
之後呼叫了內部方法[[construct]]
- Assert: F is an ECMAScript function object.
- Assert: Type(newTarget) is Object.
- Let callerContext be the running execution context.
- Let kind be F.[[ConstructorKind]].
- If kind is "base", then
- Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
- Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
- Assert: calleeContext is now the running execution context.
- If kind is "base", perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
- Let constructorEnv be the LexicalEnvironment of calleeContext.
- Let envRec be constructorEnv's EnvironmentRecord.
- Let result be OrdinaryCallEvaluateBody(F, argumentsList).
- Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
- If result.[[Type]] is return, then
- If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]).
- If kind is "base", return NormalCompletion(thisArgument).
- If result.[[Value]] is not undefined, throw a TypeError exception.
- Else, ReturnIfAbrupt(result).
- Return ? envRec.GetThisBinding().
其中第5步,會建立物件,呼叫OrdinaryCreateFromConstructor,其定義如下:
OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] )
- Assert: intrinsicDefaultProto is a String value that is this specification's name of an intrinsic object. The corresponding object must be an intrinsic that is intended to be used as the [[Prototype]] value of an object.
- Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
- Return ObjectCreate(proto, internalSlotsList).
第2步,獲取原型鏈,第三部建立物件 ObjectCreate ( proto [ , internalSlotsList ] )
- If internalSlotsList is not present, set internalSlotsList to a new empty List. Let obj be a newly created object with an internal slot for each name in internalSlotsList. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1.
- Set obj.[[Prototype]] to proto.
- Set obj.[[Extensible]] to true.
- Return obj.
哈哈哈,沒時間整理了,官方標準搬運工,有興趣的自己看官方標準www.ecma-international.org
最後放一個官方關於prototype的說明