javascript - 繼承與原型鏈

背景

看到繼承與原型鏈就會想到prototype__proto__,做爲一個菜鳥,在昨天以前只用過prototype,知道有一個叫__proto__的東西,可是不知道是作什麼的。 可是昨天遇到一個vue的繼承問題,經過instanceof 判斷不出來變量是否是經過Vue.extend生成子類,代碼以下:javascript

let component = Vue.extend({
    mounted(){
        console.log('mounted')
    }
})

console.log(component instanceof Vue) // false
複製代碼

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) // 1
// 這裏的a是實例上的屬性
console.log(ins.b) // 2
// 這裏的b是實例上的屬性
console.log(ins.c) // 4
// 這裏的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操做的時候會執行如下步驟:

  1. Assert: constructExpr is either a NewExpression or a MemberExpression.
  2. Assert: arguments is either empty or an Arguments.
  3. Let ref be the result of evaluating constructExpr.
  4. Let constructor be ? GetValue(ref).
  5. If arguments is empty, let argList be a new empty List.
  6. Else,
  7. Let argList be ArgumentListEvaluation of arguments.
  8. ReturnIfAbrupt(argList).
  9. If IsConstructor(constructor) is false, throw a TypeError exception.
  10. Return ? Construct(constructor, argList).

最後一步的Construct是這麼定義的: Construct ( F [ , argumentsList [ , newTarget ]] )

  1. If newTarget is not present, set newTarget to F.
  2. If argumentsList is not present, set argumentsList to a new empty List.
  3. Assert: IsConstructor(F) is true.
  4. Assert: IsConstructor(newTarget) is true.
  5. Return ? F.[[Construct]](argumentsList, newTarget).

以後調用了內部方法[[construct]]

  1. Assert: F is an ECMAScript function object.
  2. Assert: Type(newTarget) is Object.
  3. Let callerContext be the running execution context.
  4. Let kind be F.[[ConstructorKind]].
  5. If kind is "base", then
    • Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
  6. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
  7. Assert: calleeContext is now the running execution context.
  8. If kind is "base", perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
  9. Let constructorEnv be the LexicalEnvironment of calleeContext.
  10. Let envRec be constructorEnv's EnvironmentRecord.
  11. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
  12. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
  13. 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.
  14. Else, ReturnIfAbrupt(result).
  15. Return ? envRec.GetThisBinding().

其中第5步,會建立對象,調用OrdinaryCreateFromConstructor,其定義以下:
OrdinaryCreateFromConstructor ( constructor, intrinsicDefaultProto [ , internalSlotsList ] )

  1. 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.
  2. Let proto be ? GetPrototypeFromConstructor(constructor, intrinsicDefaultProto).
  3. Return ObjectCreate(proto, internalSlotsList).

第2步,獲取原型鏈,第三部建立對象 ObjectCreate ( proto [ , internalSlotsList ] )

  1. 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.
  2. Set obj.[[Prototype]] to proto.
  3. Set obj.[[Extensible]] to true.
  4. Return obj.

哈哈哈,沒時間整理了,官方標準搬運工,有興趣的本身看官方標準www.ecma-international.org

最後放一個官方關於prototype的說明

相關文章
相關標籤/搜索