Javascript在原型中设置值类型的对象属性?

Gri*_*Lab 10 javascript variables prototype object

我总是在对象的原型中设置具有值类型的属性,因为这会阻止每次创建对象时都必须初始化它们.

从那以后,我做了一些调试,发现如果原型属性的值会发生变化,那么会为对象分配一个新属性,并保持原型.

var o = function () {

};

o.prototype.x = 0;

o.prototype.setX = function(x) {
    this.x = x;
};

var obj = new o();
obj.setX(1);
console.log(obj);

// o {x: 1, x: 0, setX: function}
Run Code Online (Sandbox Code Playgroud)

所以我的问题是,如果您知道原型属性的值可能会发生变化,那么在原型中设置它并获得它不需要在对象创建时进行初始化是否有任何实际好处?

我的意思是当它改变为另一个值时,必须将新值分配给现有对象,从而失去了首先在原型中设置它的初始好处!此外,它现在意味着您在对象中有一个属性,在原型中有一个属性.

它在谷歌开发者说,这是这样做的方式,但我不太确定.

什么时候应该在原型中设置值类型属性,当你知道值会改变时,是否有任何实际的性能提升?

svi*_*gen 8

我实际上已经对这种事情进行了基准测试,如果内存服务,原型模型通常比直接为对象赋值更慢.例外情况是在实例大致相同的情况下,实例化的发生频率远远高于属性访问.

所以,如果你像这样定义你的对象:

var Class = function() {};
Class.prototype.p1 = 1;
Class.prototype.p2 = 1;
Class.prototype.p3 = 1;
Run Code Online (Sandbox Code Playgroud)

您可以避免在实例化时遇到性能损失,将属性复制到每个对象.但是,当访问或修改这些属性时,该性能会显示出来.而且,如果您碰巧在不修改它们的情况下访问这些属性,那么每次访问它们时都会导致原型链的性能下降(因为它们永远不会复制到本地实例).但是,如果你有很多属性,并且只为每个实例访问少量属性,这可能是理想的.

for (var i = 0; i < 100000; i++) {
  var x = new Class();
  console.log(x.p1);
  // ignore p2-p99. we don't need them right now.
}
Run Code Online (Sandbox Code Playgroud)

另一方面,如果你需要多次在少量实例中迭代属性,那么最好避免在原型链中行走.

var Class = function() {
  this.p1 = 1;
  this.p2 = 1;
  this.p3 = 1;
}
Run Code Online (Sandbox Code Playgroud)

每个实例在创建时都会获得自己的p1,p2和p3副本.当我们访问其中的任何一个时,不需要咨询原型链.

var instances = [
  new Class(), new Class(), new Class()
];

for (var i = 0; i < 1000000; i++) {
  console.log(instances[i % instances.length].p1);
  console.log(instances[i % instances.length].p2);
  console.log(instances[i % instances.length].p3);
}
Run Code Online (Sandbox Code Playgroud)

如果我以后有时间,我会稍后对此进行基准测试以验证.在那之前,我能给你的只是理论!

附录

使用原型有两个不一定与性能相关的好处.

,对于相对静态的属性(如函数),它可以节省内存.大多数应用程序不会遇到任何限制.但是,在交换时这对你来说是个问题,请使用原型.

,原型允许您为一个类的所有现有实例分配函数和属性.要使用实例级属性实现相同的功能,您需要查找并迭代.

基准

我最近的基准测试,这我只在FF和Chrome OS X的最新版本上运行,我用下面的语法单层(非继承)类定义:

原型.*

function Class() {}
Class.prototype.a = 1;
Class.prototype.b = 2;
Class.prototype.c = 3;
Run Code Online (Sandbox Code Playgroud)

这个.*

function Class() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}
Run Code Online (Sandbox Code Playgroud)

在上述两种语法之间,this.*语法的全面运行速度提高了约10.5%.

添加一个继承级别,我使用了以下内容:

原型.*

function Base() {}
Base.prototype.a = 1;
Base.prototype.b = 2;
Base.prototype.c = 3;

function Class() {}
Class.prototype = new Base();
Run Code Online (Sandbox Code Playgroud)

这个.*

function Base() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}

function Class() {
  Base.apply(this);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我发现prototype.*语法全面快了大约38.5%.对于成员访问,浏览器之间的this.*语法总和快一些; 但是,优势并不像实例化优势那样明显.

我还对混合的继承方法进行了基准测试:

function Base() {
  this.a = 1;
  this.b = 2;
  this.c = 3;
}

function Class() {
}
Class.prototype = new Base();
Run Code Online (Sandbox Code Playgroud)

总的来说,它比prototype.*语法快了约0.5%(可能微不足道).然而,有趣的是在实例化期间减慢约1%,但在成员访问期间比prototype.*语法快约2%.同样,并不是非常重要,但我不禁怀疑这些收益是否会随着遗产深度的增加而扩大.

当然,请注意,这些基准测试并非在良好控制的环境中完成.我倾向于认为性能上显着的差距是显着的.但是,较低的百分比很可能是由于我的机器上的CPU负载波动.

总而言之,我可能建议使用this.*其中没有发生继承其中成员访问比类实例化更常见的情况.当然,如果你不按业绩每盎司挤掉你的web应用程序,使用感觉更直观的为您和您的团队的语法.大多数网络应用程序将比对象构建样式的差异具有更重要的性能命中率.

例如,更改背景颜色,

document.body.style.backgroundColor = 'blue';
Run Code Online (Sandbox Code Playgroud)

... 比实例化基准测试中表现最差的构造函数大约70%.

  • 大约一年前,我做了一些类似的测试,发现在Chrome中,对原型对象的查找实际上比直接在对象上查找更快.这很奇怪,但非常一致.我不知道是否仍然如此,但很明显V8对原型查找进行了一些优化.对于这些JIT编译器,性能问题变得更加复杂. (2认同)