JS原型链和继承网上已经烂大街了,5毛能够买一堆,这里只提一下:javascript
constructor:普通对象和函数对象都有,指向建立它的函数
prototype: 函数对象才有,指向构造函数的原型对象(另外一个普通对象)
__proto__: 普通对象和函数对象都有,指向建立它的构造函数的原型对象java
function Fun1(){}; Fun1.prototype.constructor == Fun1 //true var f2 = new Fun1(); f2.__proto__ == Fun1.prototype //true
当实例化新对象时,JS将使用与来自相同构造函数的先前对象相同的初始隐藏类建立它们。
当添加属性时,对象从一个隐藏类转换为另一个隐藏类,一般遵循所谓的“转换树”中的先前转换。例如,若是咱们有如下构造函数:缓存
function fun1(){ this.a = 1; this.b = 2; }
建立过程大体以下(对于普通对象{a:1, b:2}也会有相似的过程):
fun1: M0 {}
|
this.a: M1 {a:?}
|
this.b: M2 {a:?,b:?}函数
在第二次用一样的构造函数建立一个新的对象实例时(如: var o2 = new fun1()),会复用M0,M1和M2。性能
这种机制有下面几个优势:优化
能够参考以下:
M0
{}
|
M1
{a:?}
/ \
M2 M3
{a:?,b:?} {a:?,c:?}this
与实例的建立过程不一样,原型的建立过程并不会使用隐藏类,由于原型prototypes一般状况下是一个惟一的对象,而且不会被其余的不一样对象所共享结构。prototype
Prototype建立有二个主要的阶段:code
function Foo() {} // Prototype在'创建'模式 var proto = Foo.prototype; proto.method1 = function() { ... } proto.method2 = function() { ... } var o = new Foo(); // 从'创建'模式切换到'使用'模式 o.method1(); // 一样切换到'使用'模式 proto.method1.call(o);
那么了解上面的原型建立过程有什么用呢?
JS很难在编译阶段进行代码分析,即便某个类被用做原型。当咱们发现一个类被当作原型,如:对象
var o = {x:1}; func.prototype = o;
由于原型是能够改变的,所以以上代码并不能肯定O被用做原型,除非直到全部步骤结束。
因此:JS不得不首先进行隐藏类的建立过程,并转化为原型创建过程,这很是消耗性能。
那么应该怎么作呢?以下:
var o = {}; func.prototype = o; o.x = 1;
若是一个对象要做为原型,那么尽可能在给对象添加属性以前就把该对象赋给原型属性。
更好的方式是下面这种:
func.prototype = Object.create(…); func.prototype.method1 = … func.prototype.method2 = …
最后,优雅的写法是:
var proto = func.prototype = Object.create(…); proto.method1 = … proto.method2 = …