背景
看到继承与原型链就会想到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的说明