用于执行一致性和性能的原型或闭包?

时间:2013-05-20 07:29:30

标签: javascript performance closures

在javascript中调用方法的性能执行和执行时间一致性之间的最佳折衷是什么?

我还在学习javascript,并且会将原型用于大多数事情(即Brendan Eich here),但我认为我发现函数闭包的性能和一致性更好(我知道我可能已经过度优化了) 。我一直在测试的一种原型模式:

function PrototypeA() {}
PrototypeA.prototype.a = 0;
PrototypeA.prototype.b = 0;
PrototypeA.prototype.someMath = function() {
    this.a += 1;
    this.b += 2;
};
var Test = new PrototypeA();
Test.someMath();

在阅读完这篇文章(很棒!)post后决定基准闭包。目前的关闭模式我正朝着:

var SubModule = new((function() {
    var a = 0;
    var b = 0;
    var someMath = function() {
        a += 1;
        b += 2;
    };
    return function() {
        this.someMath = someMath
    };
})())();

我发现由于测试设置中的错误(例如在基准测试中调用构造函数或以导致不同范围遍历路径的方式调用方法),各种模式的许多基准都会产生误导。我只是想测试方法执行。

在本地进行了大量的基准测试(基准测试,许多不同的模式)之后,我提出了值得注意的模式:

Jsperf Here

我的发现似乎支持组织良好的闭包不会在执行时间上偏离其他类型的对象(我松散地使用术语“类型”)。我意识到由于基准测试或设置上的任何数字或错误,我可能完全错误。

感谢您的期待!

2 个答案:

答案 0 :(得分:10)

由于您的用例是动画,因此您希望坚持使用闭包。使用javascript的简单经验法则是“.越多,代码越慢”。在jsperf上发布的示例中,最慢的是那些引用this的示例。 每次时间,引擎必须查看当前对象并确定您尝试访问的内容是否立即可用于对象,以及是否必须查找原型链。在关闭方面,你引用的是一个相当有限的范围,由你编写的函数预先确定,引擎要做的最多就是能够查看该范围以引用其中的变量。

现在,有一些问题。首先,当您仅引用一次或两次时,this的使用速度会明显加快。在这个例子中并非如此,但值得一提。添加对this的引用的次数越多,原型依赖代码就越慢。查看this simple perf的示例。其次,如果你构建了很多这些模块(例如,如果你正在构建一个数据网格),那么关闭时构造速度非常慢,并且浪费了大量内存。有关速度差异,请参阅this example。您添加的方法和属性越多,它就越糟糕。这是因为每次创建一个新的闭包都会产生独特的功能和属性,而基于原型的闭包只会重复使用相同的东西。

在任何一种情况下,我的谦虚建议只是看看你正在编程的情况。大多数时候,使用原型是要走的路 - 可读性[在某些情况下],可重用性,可扩展性......但如果它们太慢,那么你就是不能使用它们。话虽这么说,将这两个概念结合起来给你提供一个可重复使用的模块化设置并不会经常受到影响的方法是没有错的。例如,尝试使用'Mixins':

var Mixin = {
    interval: 100,
    finish: function () { window.clearInterval(this.intervalRef); },
    start: function (callback) { this.intervalRef = window.setInterval(callback, this.interval)
};

var Module = (function () {
    function getMessage () {
        return "weee!";
    }
    var somethingThatReallyNeedsSpeed = function () {
        var iterations, self = this;

        this.start(function () {
            console.log(getMessage());
            iterations++;
            if(iterations > 10000) {
               self.finish();
            }
        });
    }
    somethingThatReallyNeedsSpeed.prototype = Mixin;
    return somethingThatReallyNeedsSpeed;
})();

在这个例子中,由于原型被设置为引用Mixin对象,因此构造速度不会受到影响 - 有两种方法和特性函数不需要构造的属性时间,但它仍然可以通过this

</rant>

答案 1 :(得分:3)

范围链查找通常比原型链查找更快:Scope Chain Lookup vs Prototype Chain Lookup · jsPerf

要记住几点:

  1. 不要在函数的prototype上声明变量。它会导致意想不到的问题。只能在prototype上声明函数。
  2. 如果您希望函数的所有实例共享变量,请将其声明为函数本身的属性。
  3. 不要写new((function() { ... })())()之类的代码,除非你希望别人跟你们分享干草叉和火把。