【问题标题】:Is a closure for dereferencing variables useful?取消引用变量的闭包有用吗?
【发布时间】:2023-03-20 21:00:02
【问题描述】:

我不确定取消引用变量是否有用或何时有用(提高性能)。

var x = a.b.c.d[some_key].f;
while (loop) {
    do_something_with(x);
}

似乎比

while (loop) {
    do_somthing_with(a.b.c.d[some_key].f);
}

这是需要还是由智能 JavaScript 引擎自动完成?

但我的实际问题是我是否应该这样做,例如,在图书馆里。

(function() {
    var slice = Array.prototype.slice;

    Function.prototype.x = function x() {
        var args = slice.call(arguments, 0);
        ...
    };
})();

或者只是

Function.prototype.x = function x() {
    var args = Array.prototype.slice.call(arguments, 0);
    ...
};

引擎无法自动改进这一点,因为它不知道Array.prototype.slice 是否会在运行期间发生变化。

那么:创建用于创建切片函数的本地引用的闭包是否会使脚本更快?或者额外的闭包范围是否比访问 Array 的属性“prototype”的属性“slice”慢?

【问题讨论】:

  • 仅供参考,应该是.slice.call(arguments, 0)
  • jsperf.com 除非您对创建的变量数量感到非常疯狂,否则我会认为这是浏览器环境中的微优化。
  • ...或只是[].slice.call( arguments )
  • 仅供参考,a.b.c.d["e"].f 等于 a.b.c.d.e.f
  • 过早的微优化是不好的。尤其是在没有真正需要的情况下。不过在图书馆里可能没问题。

标签: javascript closures dereference


【解决方案1】:

“取消引用”实际上是一个令人困惑的词。不是这样,你只是 cache 在一个局部变量中的一些属性/方法。无论您是访问随机对象上的某些属性/方法还是使用Array.prototype.slice,实际上都没有区别。当您不止一次访问这些深层嵌套的属性时,它非常很有意义。

Tbh,“现代”浏览器确实对访问进行了相当多的优化。所有现代 js 引擎都使用内部查找表来访问属性。但是,您仍然希望缓存那些深层嵌套的东西,因为在旧引擎中,它会一直向下通过所有涉及的对象来解决它。

使用本地缓存引用的另一个原因是,即使是现代 js 引擎也不会在使用某种显式或隐式 eval 机制后立即使用哈希查找。

尤其是 Internet Explorer


请注意:不太建议对对象方法使用本地缓存(就像使用 slice 方法一样)。许多对象使用this 来确定它们被调用的上下文。将方法存储在局部变量中会导致 this 绑定到 global objectnull。 因此,请务必确保使用 method.call 调用此类方法以手动设置上下文。

【讨论】:

  • 按照 Jared Farish 的建议,我在 jsperf.com/prop-acess-vs-closures 设置了一个测试用例。跨链时,Firefox 和 Opera 似乎都更快。但是:在我添加第三个测试用例之前,链和闭包方法完全相等。
  • @Bergi:那个测试用例不是很有用。正如我所提到的,如果您只想访问一次嵌套的属性/方法,那么访问它当然会更快。该测试用例调用函数,获取引用然后访问引用。必须更慢,无需进行基准测试。
【解决方案2】:

如果您打算多次访问某个属性,请考虑将其分配给局部变量。然而,对于现代 javascript 引擎来说,这种微优化几乎没有什么区别,最重要的是编写表达您意图的代码。

【讨论】:

    【解决方案3】:

    对于这个特定的问题,无论如何你都希望有一个实用函数:

    function toArray( arrayLike ) {
        return Array.prototype.slice.call( arrayLike );
    }
    

    ...或者如果您关心性能:

    var toArray = (function () {
        var slice = Array.prototype.slice;
    
        return function ( arrayLike ) {
            return slice.call( arrayLike );
        };
    })();
    

    您不希望在您的代码中使用 slice.call 构造...

    【讨论】:

      猜你喜欢
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-19
      • 2013-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多