6

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

我仍在学习 javascript,并且会为大多数事情使用原型(即此处的 Brendan Eich ),但我认为我从函数闭包中找到了更好的性能和一致性(我知道我可能过度优化了)。我一直在测试的一种原型模式:

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

在阅读这篇(很棒的!)帖子后决定关闭基准。我正在转向的当前关闭模式:

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 在这里

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

感谢您的关注!

4

2 回答 2

10

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

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

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

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>

于 2013-05-20T08:50:24.207 回答
3

作用域链查找一般比原型链查找更快:作用域链查找 vs 原型链查找·jsPerf

要记住的几点:

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