背景
看到繼承與原型鏈就會想到prototype
與__proto__
,做爲一個菜鳥,在昨天以前只用過prototype
,知道有一個叫__proto__
的東西,可是不知道是作什麼的。 可是昨天遇到一個vue的繼承問題,經過instanceof
判斷不出來變量是否是經過Vue.extend
生成子類,代碼以下:javascript
let component = Vue.extend({
mounted(){
console .log('mounted' )
}
})
console .log(component instanceof Vue)
複製代碼
vuejs官方文檔是這麼介紹vue.extend
這個方法的vue
既然是"子類"爲何instanceof的結果是false。java
帶着這個問題,開始深刻學習一下js的繼承與原型鏈。bash
學習
說到繼承與原型鏈,不得不先放張圖鎮樓學習
這張圖看不懂不要緊,一步一步慢慢來。ui
先看下面代碼this
function Test ( ) {
this .a = 1
this .b = 2
}
let ins = new Test()
Test.prototype.b = 3
Test.prototype.c = 4
console .log(ins.a)
console .log(ins.b)
console .log(ins.c)
複製代碼
依次打印這幾個變量,會有如下這樣的結果lua
-> 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生成一個實例的過程是這樣的:spa
// js代碼
var o = new Foo();
// js實際執行代碼
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
複製代碼
這段代碼說明讓我產生了極大的興趣,下面是梳理ecma官方標準 prototype
當執行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的說明