Javascript 在原型中设置具有值类型的对象属性? [英] Javascript setting object properties with value type in the prototype?

查看:63
本文介绍了Javascript 在原型中设置具有值类型的对象属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我总是在对象的原型中使用值类型设置属性,因为这样可以避免每次创建对象时都必须初始化它们.

I always have set properties with a value type in the prototype of the object as this stops them having to be initialized every time the object is created.

从那时起,我进行了一些调试,发现如果原型属性的值会发生变化,则会为该对象分配一个新属性,而原型则保持不变.

Since then I have done some debugging and have found that if the value of that prototype property is ever going to change, then a new property is assigned to the object and the prototype one stays put.

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}

所以我的问题是,如果您知道原型属性的值可能会发生变化,那么在原型中设置它并从中受益而不必在创建对象时进行初始化是否有任何真正的好处?

So my question is if you know that the value of the prototype property is likely to change then is there any real gain in setting it in the prototype and benefiting from it not having to be initialized on object creation?

我的意思是,当它更改为另一个值时,无论如何都必须将新值分配给现有对象,从而失去最初在原型中设置它的初始好处!此外,现在这意味着您对同一事物有 2 个属性,一个在对象中,一个在原型中.

What I mean is when it changes to another value, the new value has to be assigned anyway to the existing object losing the initial benefit of setting it in the prototype in the first place! Furthermore it now means that you have 2 properties for the same thing one in the object and one in the prototype.

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

It says on google developers that this is the way to do it, but I am not so sure.

什么时候应该在原型中设置值类型属性,当您知道值无论如何都会改变时,是否有任何真正的性能提升?

When should value type properties be set in the prototype and is there any real performance gain when you know the value is going to change anyway?

推荐答案

我实际上已经对这类事情进行了基准测试,如果没记错的话,原型模型通常比直接为对象赋值要慢.例外情况是实例在很大程度上相同并且实例化发生的频率远高于属性访问.

I've actually benchmarked this sort of thing, and if memory serves, the prototypal model is usually slower than assigning values directly to an object. The exception would be in cases where instances are largely identical and instantiating occurs far more frequently than property access.

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

So, if you define your objects like this:

var Class = function() {};
Class.prototype.p1 = 1;
Class.prototype.p2 = 1;
Class.prototype.p3 = 1;

您可以避免在实例化时性能下降,将属性复制到每个对象.但是,当访问或修改这些属性时,这种性能就会显现出来.而且,如果您碰巧访问这些属性而没有修改它们,则每次访问它们都会导致性能下降(因为它们永远不会复制到本地实例).但是,如果您有许多属性并且每个实例只访问其中的一小部分,这可能是理想的.

You avoid a performance hit at instantiation time, copying the properties to each object. But, That performance shows its head when those properties are accessed or modified. And, if you happen to access those properties without modifying them, you incur the performance hit of walking the prototype chain every time you access them (because they're never copied to the local instance). But, if you have many properties and only access a small handful of them for each instance, this is probably ideal.

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.
}

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

On the other end of the spectrum, if you need to iterate over the properties in a small handful of instances many times, you're better off avoiding the hit of walking the prototype chain.

var Class = function() {
  this.p1 = 1;
  this.p2 = 1;
  this.p3 = 1;
}

每个实例在创建时都有自己的 p1、p2 和 p3 副本.当我们访问它们中的任何一个时,都不需要参考原型链.

Each instance gets its own copy of p1, p2, and p3 at creation time. And the prototype chain doesn't need to be consulted when we access any of them.

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);
}

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

If I have time later, I'll benchmark this later to verify. Until then, all I can give you is the theory!

附录

使用原型有两个与性能无关的好处.

There are two not-necessarily-performance related benefits to using the prototype.

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

One, for relatively static properties (like functions), it conserves memory. Most applications don't bump up against any limits. But, on the off-change this is an issue for you, use the prototype.

,原型允许您将函数和属性分配给一个类的所有现有实例.要使用实例级属性完成相同的壮举,您需要查找并进行迭代.

Two, the prototype allows you to assign functions and properties to all existing instances of a class. To accomplish the same feat with instance-level properties, you'd need to find and iterate.

基准

根据 我的最新基准,我只在最新版本上运行对于 OS X 的 FF 和 Chrome,我对单层(非继承)类定义使用了以下语法:

Per my latest benchmarks, which I've only run on the latest versions of FF and Chrome for OS X, I used the following syntax for single-layer (non-inherited) class definitions:

原型.*

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

这个.*

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

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

Between the above two syntaxes, the this.* syntax operates about 10.5% faster all-around.

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

Adding a level of inheritance, I used the following:

原型.*

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

function Class() {}
Class.prototype = new Base();

这个.*

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

function Class() {
  Base.apply(this);
}

在这种情况下,我发现 prototype.* 语法的速度提高了大约 38.5%.this.* 语法仍然稍微更快地在浏览器之间用于成员访问;但是,这种优势几乎没有实例化优势那么明显.

And in this case, I found the prototype.* syntax to be about 38.5% faster all-around. The this.* syntax was still slightly faster totalled between browsers for member access; but, the advantage wasn't nearly as noticeable as the instantiation advantage.

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

I also benchmarked a hybrid approach to inheritance:

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

function Class() {
}
Class.prototype = new Base();

总体而言,它的运行速度比 prototype.* 语法快 0.5%(可能无关紧要).然而,有趣的是,它在实例化期间慢了大约 1%,但在成员访问期间比 prototype.* 语法快了大约 2%.同样,不是很重要,但我不禁怀疑这些收益是否会随着继承深度的增加而扩大.

Overall, it ran about 0.5% faster than the prototype.* syntax (probably insignificant). However, it was interestingly about 1% slower during instantiation, but about 2% faster during member access than the prototype.* syntax. Again, not terribly significant, but I can't help wonder whether those gains would scale as inheritance depth increases.

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

Be aware, of course, that these benchmarks weren't done in a well-controlled setting. I tend to see the noticeably wide gaps in performance as being significant. But, the lower percentages could very well be due to fluctuating CPU load on my machine.

说了这么多,我可能会建议在没有发生继承的情况下使用 this.* 其中成员访问很远比类实例化更常见.当然,如果您不急于从 Web 应用程序中榨取每一盎司的性能,请使用对您和您的团队来说感觉更直观的语法.与对象构建风格的差异相比,大多数网络应用对性能的影响远为.

All that said, I would probably advise using the this.* in cases wherein no inheritance is occurring or wherein member access is far more common than class instantiation. And of course, if you're not pressed to squeeze every ounce of performance out of your web app, use the syntax that feels more intuitive to you and your team. Most web-apps will take on much more significant performance hits than the difference in object-building styles.

例如,改变背景颜色,

document.body.style.backgroundColor = 'blue';

...比实例化性能最差的构造函数大约慢70%.

... is roughly 70% slower than instantiating the worst-performing constructor I benchmarked.

这篇关于Javascript 在原型中设置具有值类型的对象属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆