JavaScript 原型和对象建立底层原理

1. prototype/__proto__/constructor

JS原型链和继承网上已经烂大街了,5毛能够买一堆,这里只提一下:javascript

constructor:普通对象和函数对象都有,指向建立它的函数
prototype: 函数对象才有,指向构造函数的原型对象(另外一个普通对象)
__proto__: 普通对象和函数对象都有,指向建立它的构造函数的原型对象java

function Fun1(){};
Fun1.prototype.constructor == Fun1 //true
var f2 = new Fun1();
f2.__proto__ == Fun1.prototype //true

2. 对象实例的建立过程

当实例化新对象时,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 o = new fun1()),会使用一个附加在fun1上的没有任何属性的初始隐藏类M0。
  • 当添加一个属性a时,会从隐藏类M0转变为M1以描述新的属性a。
  • 当再次添加属性b时,会获得M2来体现属性a和b。

在第二次用一样的构造函数建立一个新的对象实例时(如: var o2 = new fun1()),会复用M0,M1和M2。性能

这种机制有下面几个优势:优化

  1. 只有第一次建立对象实例时会有比较大的性能开销,接下来若是再次建立实例时,将会复用以前的缓存,速度很快。
  2. 这种方法建立的对象实例一般比采用建立整个属性字典的对象实例要小,仅须要存储值在对象实例中而没必要存储属性相关信息。
  3. 使用惟一的位置来存储隐藏类,能够很快的被再次使用。

能够参考以下:
     M0
     {}
     |
     M1
    {a:?}
    /  \
   M2  M3
{a:?,b:?}  {a:?,c:?}this

3. prototype的建立过程

与实例的建立过程不一样,原型的建立过程并不会使用隐藏类,由于原型prototypes一般状况下是一个惟一的对象,而且不会被其余的不一样对象所共享结构。prototype

  • 不一样对象将拥有不一样的原型,共享原型并不会带来性能上的收益,所以没有必要。
  • 原型一般只会建立一次,为原型建立隐藏类只会带来不少无用的内存碎片。

Prototype建立有二个主要的阶段:code

  1. 创建
    原型在创建阶段使用dictionary键值对存储,速度很是快。对比隐藏类的创建过程,使用键值对存储不会切换到底层运行环境。
  2. 使用
    任何对原型的访问,或者经过原型链的访问,都会切换到使用阶段。
function Foo() {}
// Prototype在'创建'模式
var proto = Foo.prototype;
proto.method1 = function() { ... }
proto.method2 = function() { ... }

var o = new Foo();
// 从'创建'模式切换到'使用'模式
o.method1();

// 一样切换到'使用'模式
proto.method1.call(o);

4. prototype优化

那么了解上面的原型建立过程有什么用呢?
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 = …
相关文章
相关标签/搜索