Prototype
与 __proto__
咱们先写下一行代码:javascript
function Parent {}
当咱们写下这简单的一行代码时,实际上发生了两件事情java
Parent
prototype
以下图:segmentfault
构造函数 Parent
中 有一个 prototype
的属性指向 Parent
的 原型对象 prototype
原型对象 prototype
则有一个 constructor
的属性 指向回 构造函数 Parent
浏览器
紧接着,咱们又写下一行代码:函数
var parent = new Parent()
此时,图片上多出一个新成员this
注意到图中的 Parent
的实例 parent
里,有一个[[prototype]]
,为何这里不是 __proto__
呢?spa
其实,这里的 [[prototype]]
表示一种标准规范内置属性,被一些浏览器本身经过__proto__
实现了,对于 Chrome
的实现来讲,这个 __proto__
也并不存在于 实例 parent
中,而是 Object.prototype
的一个 存取描述符
,如下代码能够证实:prototype
parent.hasOwnProperty('__proto__') // false Object.prototype.hasOwnProperty('__proto__') // true Object.getOwnPropertyDescriptor(Object.prototype, '__proto__') /** * { * configurable: true, * enumerable: false, * get: f __proto__() * set: f __proto__() * } */
咱们之因此能经过 parent.__proto__
访问到,是由于经过原型链访问到了 Object.prototype
上的 __proto__
存取描述符。设计
如下内容更像是《JavaScript高级程序设计》的笔记,主要提炼出每一个继承的特色以及例图。code
function Parent() {} function Child() {} var parent = new Parent() Child.prototype = parent var child = new Child()
此时,根据第一部分所描述的细节,咱们很快能够画出这几行代码所作的事情:
这样 child
就能够经过原型链继承的方式访问到 parent
以及 Parent.prototype
上的属性和方法了。 这种方式的特色是:
function Parent(name){ this.name = name } function Child(name){ Parent.call(this, name) } var child1 = new Child('child1') var child2 = new Child('child2')
能够看到,这种方式和 原型 没有任何关系,因此画出的图也很纯粹:
这种方式的特色是:
顾名思义,就是讲上述两种继承方式有机结合,经过将方法定义在 prototype
中,属性经过借用构造函数继承的方式实现继承。
function Parent(name) { this.name = name } Parent.prototype.talk = function () {} function Child(name) { Parent.call(this, name) } var parent = new Parent('parent') Child.prototype = parent Child.prototype.constructor = Child var child = new Child('child')
此时,关系图有了一些变化:
咱们能够从图中看到,实例 child
和 实例 parent
各自拥有独立的 namne
,可是共享 Parent.prototype
中的 talk()
方法。这种方式的特色是:
Parent
function createObject(o) { function F() {} F.prototype = o return new F() } function Parent() {} var parent = new Parent() var child = object(parent)
这里先建立了一个 createObject
函数,其实就是 ES5 Object.create
的模拟实现,将传入的对象做为建立的对象的原型。
和 原型链继承
对比一下,咱们发现实际上是同样的,除了能够不用建立一个自定义构造函数 Child
。因此特色和 原型链继承
相同:
在 原型式继承
的基础上,建立一个仅用于封装继承过程的函数,该函数在内部以某种形式来作加强对象,最后返回对象。
function createObject(o) { function F() {} F.prototype = o return new F() } function enhanceObject(o) { var clone = createObject(o) clone.talk = function() {} return clone } function Parent() {} var parent = new Parent() var child = enhanceObject(parent)
经过加强对象,每次建立的新实例,所拥有的方法不是共享 Parent.prototype
中的,而是各自独立建立的。所以,该方式的特色相似借用构造函数继承
:
咱们在 组合继承
中发现,这种方式最大的缺点是会调用两次父构造函数,
一次是设置子类型实例的原型的时候:
var parent = new Parent('parent') Child.prototype = parent
一次在建立子类型实例的时候:
var child = new Child('child')
回想下 new 的模拟实现,其实在这句中,咱们会执行:
Parent.call(this, name)
因此咱们在例图中能够发现,parent
和 child
中都有一份 name
属性。
所以,经过 在 寄生组合式继承
中的 createObject
方法,间接的让 Child.prototype
访问到 Parent.prototype
,从而减小调用父构造函数的次数。
function createObject(o) { function F() {} F.prototype = o return new F() } function Parent(name) { this.name = name } function Child(name) { Parent.call(this, name) } Child.prototype = createObject(Parent.prototype) Child.prototype.constructor = Child var child = new Child('child')
例图以下:
这种方式的高效率体现它只调用了一次 Parent 构造函数,而且所以避免了在 Parent.prototype 上面建立没必要要的、多余的属性。与此同时,原型链还能保持不变;所以,还可以正常使用 instanceof 和 isPrototypeOf。开发人员广泛认为寄生组合式继承是引用类型最理想的继承范式。
从 Prototype 开始提及 一共分为两篇,从两个角度来说述 JavaScript 原型相关的内容。