【问题标题】:Prototypes or closures for execution consistency and performance? [closed]执行一致性和性能的原型或闭包? [关闭]
【发布时间】:2013-05-20 07:29:29
【问题描述】:

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

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

经过大量本地基准测试(benchmark.js 跨越许多不同的模式),我提出了值得注意的模式:

Jsperf Here

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

感谢收看!

【问题讨论】:

  • 闭包和原型是完全不同的动物。很难想象您会想要或需要在它们之间做出选择的情况。
  • var x = new((function() {...})())(); 是一个可怕的结构。
  • 动画是我的用例。 SubModule 结构很丑陋。它试图避免对象文字和额外的代码行。
  • new 是不必要的。尝试简化为var x = (function() {...})();
  • 如果不创建对象,我不会得到闭包。有没有其他方法可以在没有 new 的情况下从该构造函数创建闭包?

标签: javascript performance closures


【解决方案1】:

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

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

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

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>

【讨论】:

  • 确实有助于澄清事情。谢谢。
  • 我不确定这个答案是否正确或至少是否正确。当创建具有共享结构的不同对象时,原型通常更快,因为它们允许更好的优化。请参阅此示例:jsperf.com/prototype-vs-closures/58
  • @JanOlafKrems 你不是在比较同一件事。在您提供的演示中,您正在调用 get_name 或任何引用一次变量的内容。在我所说的内容中,有很多对变量的引用(无论是在闭包中还是在 this 上)。使用原型的一个引用比使用闭包更快,但是添加更多对this 的引用会减慢速度。
  • 修复了您的 JSPerf 以实际使用原型(创建新对象)。原型更快,即使对this 的许多访问:jsperf.com/simple-closure-vs-prototype/10
  • 不,你真的没有解决任何问题。您更改了测试的参数。测试的不是创建对象后访问的速度有多快,而是访问的速度有多快。我已经说过,创建每次都定义其功能的对象要慢得多。您的新测试不会在讨论中添加任何新内容。
【解决方案2】:

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

需要记住的几点:

  1. 永远不要在函数的prototype 上声明变量。它会导致意想不到的问题。只有函数必须在 prototype 上声明。
  2. 如果您希望函数的所有实例共享一个变量,请将其声明为函数本身的属性。
  3. 永远不要编写像 new((function() { ... })())() 这样的代码,除非您希望人们用干草叉和火把追随您。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-17
    • 1970-01-01
    相关资源
    最近更新 更多