Javascript函数`Recreation'?

时间:2011-04-25 01:49:46

标签: javascript performance function scope

我阅读了很多JavaScript代码,看到了许多不同风格的制作所谓的类。我正在开发一个轻量级DOMish类,其中包含我在Node.JS上运行的模板脚本的最小值(使用JSON将DOMish实例转换为另一个DOMish实例,然后将其序列化为HTML,缓存原始DOMish实例并按模板请求克隆它。

以上与我的问题无关。阅读http://www.phpied.com/3-ways-to-define-a-javascript-class/后的第1.2节。内部定义的方法

  

1.1的缺点。是每次创建新对象时都会重新创建方法getInfo()。

如第1.1节所述,定义类的方法是(略微修改以反映我自己的情况,使用本地/私有变量):

function Apple (type) {
    var that = this;

    // local-variable
    var color = 'red';

    // local-(extern)-function
    var infoProvider = function() { // inner-function
        // note the danger of this and that!
        return that.color + ' ' + that.type + ' ' + this.type;
    };

    this.__defineGetter__("type", function() { return type; });

    this.getInfo = function(otherContext) {
        return infoProvider.call(otherContext); // other "this" scope
    };
}

var apple = new Apple('iPod');
apple.getInfo({type: 'music player'}); // 'red iPod music player'

我碰巧使用相同的样式,因为函数现在可以访问构造函数内定义的本地/私有变量和函数。但每次创建新对象时都会重新创建句子“[The function]”。吓到我了(表现明智的)!当我使用使用V8的Node时,我总是认为只使用不同的上下文(this es和{{1 }}为s)。

我应该害怕“娱乐”吗?与基于原型的功能相比有多糟糕?或者这是纯粹的审美(人们喜欢把东西放在一起,在构造函数中,VS,人们喜欢原型)?

2 个答案:

答案 0 :(得分:3)

虽然V8确实缓存了很多东西,但是这不是其中一种情况,可以通过以下代码来证明:

> function foo(){ this.bar = function(){ return this.x; }; this.x = Math.random(); }
> var ary = [];
> for(var i=0; i<1000000; i++){ ary.push(new foo()); }

以上使用144MB内存。

> function foo(){ this.x = Math.random(); }
> foo.prototype.bar = function(){ return this.x; }
> var ary = [];
> for(var i=0; i<1000000; i++){ ary.push(new foo()); }

这使用68MB的内存。

请注意:V8源头位于:

http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/compilation-cache.h?r=5284

似乎暗示可能确实缓存了所述函数的编译:

  

编译缓存保持共享   已编译脚本的函数信息   和逃避。共享功能信息   使用源查找   字符串作为关键。经常   表达式编译数据是   缓存。

但是测试显示,即使编译编译,仍然会创建新对象,就好像你没有创建新对象一样,你会破坏JS:)

修改

进一步测试:

function foo(){
    this.bar = function(){
        this.x = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';
        return this.x;
    };
    this.x = Math.random();
}
var ary = [];
for(var i=0; i<1000000; i++){ ary.push(new foo()); }

使用144MB内存。由于我添加了100个char字符串,如果函数本身没有被缓存,我们将使用额外的100MB左右的内存。

所以上面说明是的,函数本身是缓存的。但它仍然需要由一个新对象代表。

答案 1 :(得分:2)

以下代码:

this.getInfo = function(otherContext) {
    return infoProvider.call(otherContext); // other "this" scope
};

...创建一个新函数并将其引用分配给新创建的对象的getInfo属性。每次调用new Apple时都会发生这种情况。每个新实例都有自己的副本。

但是,这段代码:

Apple.prototype.getInfo = function(otherContext) {};

...创建一个getInfo函数的共享实例,分配给构造函数的原型。

这种差异不仅仅是美学上的。这是创建实例方法的一种根本不同的方式。函数就像任何其他对象一样(即您可以为其指定属性)。例如,您可以这样做:

this.getInfo.classVariable = "whatever" 

这将仅在一个实例的getInfo函数上创建一个新属性。如果该函数被缓存,那么该赋值将影响所有实例,这不是所希望的。