javascript - 继承与原型链

背景

看到继承与原型链就会想到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操作的时候会执行以下步骤:

  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的说明

转载于:https://juejin.im/post/5c7a3dfee51d4553d7647c3f

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值