标签:没有 转换 共享 结束 步骤 相同 create 不能 应该
JS原型链和继承网上已经烂大街了,5毛可以买一堆,这里只提一下:
constructor:普通对象和函数对象都有,指向创建它的函数
prototype: 函数对象才有,指向构造函数的原型对象(另一个普通对象)
__proto__: 普通对象和函数对象都有,指向创建它的构造函数的原型对象
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:?}
与实例的创建过程不同,原型的创建过程并不会使用隐藏类,因为原型prototypes通常情况下是一个唯一的对象,并且不会被其他的不同对象所共享结构。
Prototype创建有二个主要的阶段:
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 = …
标签:没有 转换 共享 结束 步骤 相同 create 不能 应该
原文地址:https://www.cnblogs.com/full-stack-engineer/p/9644012.html