22

我在javascript中工作了一段时间,通常做这样的事情只是为了缓存在深层结构或“命名空间”中声明的函数的属性值

//global scope
(function ($, lib) {
  //function scope 1
  var format = lib.format, // instead of calling lib.format all the time just call format
    touch = lib.pointer.touch, //instead of calling lib.pointer.touch each time just touch
    $doc = $(document),
    log = logger.log; //not console log...


  $doc.on('app:ready', function () {
    //function scope 2
    $doc.on('some:event', function (e) {
      //function scope 3
      //use the cached variables
      log(format('{0} was triggered on the $doc object', e.type);
    });

    $doc.on(touch, function (e) {
      //function scope 3
      log(format('this should be {1} and is... {0} ', e.type, touch);
    });
  }); 
}(jQuery, lib));

我这样做是因为:

  • 尽管我很懒惰,但编写 touch 似乎比编写 lib.pointer.touch 更有吸引力,即使具有精美自动完成功能的强大 IDE 可以对此有所帮助,但 touch 更短。
  • 最小化器可以将单个私有变量转换为单个字母变量,所以这对我来说也很有意义(我知道,我知道,永远不要过早优化,但我猜这似乎是安全的)

以这种方式编写的所有代码似乎在移动设备和桌面浏览器上都表现得不错,所以这似乎是一种安全的做法(在“实践”中,双关语)。但是我因为这依赖于闭包,并且内部函数必须创建一个闭包来保存它声明的上下文我想知道......

  • 如果一个函数不使用外部上下文中的变量(自由变量)......闭包上下文是否仍然保存?(或者如果 ia 树掉在树林里并且没有人听到它,它还会发出崩溃的声音吗?呵呵)我知道这可能因 javascript 引擎而异,因为 ECMA 没有提及是否需要保存当不访问来自外部的变量时,上下文与否。

  • 如果上面的表达式为真......这段代码会更有效率吗?

    //global scope
    (function ($, lib) {
      //function scope 1
      var format = lib.format,
        touch = lib.pointer.touch,
        $doc = $(document),
        log = console.log;
    
      $doc.on('app:ready', function () {
    
        (function ($doc, touch, lib, format) {
          // since all the variables are provided as arguments in this function
          // there is no need to save them to the [[scope]] of this function
          // because they are local to this self invoking function now
          $doc.on('some:event', function (e) {
            //function scope 3
            //use the cached variables
            log(format('{0} was triggered on the $doc object', e.type);
          });
    
          $doc.on(touch, function (e) {
            //function scope 3
            log(format('this should be {1} and is... {0} ', e.type, touch);
          });
        }($doc, touch, lib, format));      
    
      }); 
    
    }(jQuery, lib));
    

它是否更有效,因为它将这些变量传递给自我立即调用函数?创建该新功能的成本是否会对代码产生任何影响(负面或正面)?

  • 如何以可靠的方式正确测量我的 javascript 库的内存消耗?我有 100 个小 javascript 模块,它们都在立即自调用函数中,主要是为了避免变量泄漏到全局上下文中。所以它们都封装在与上面提到的代码块非常相似的模块中。

  • 将变量缓存得更近会产生更好的效果,即使这意味着我将不得不在更接近它们将要使用的位置重复变量的声明?

  • 我有一种感觉,寻找不在当前本地上下文中的变量,引擎将首先查看父范围并迭代该级别的所有变量......每个级别的变量越多,性能看起来可能越差对于一个变量。

  • 尝试从内部闭包中找到一个未定义的变量将是最昂贵的,因为根据定义,该变量将首先在父作用域上搜索,直到全局作用域,而没有找到它将迫使引擎最终到达全局作用域。真的吗?引擎是否优化了这种查找?

最后......我知道我不想将我的代码作为第二个示例来实现,主要是因为它会使代码更难阅读,而且我对使用第一种方法最小化最终输出的大小感到有点满意。我的问题是出于好奇,并试图更好地理解javascript这个非常好的特性。

据此测试...

http://jsperf.com/closures-js

似乎第二种方法更快。但是只有在迭代疯狂的次数时才明显......现在我的代码没有进行那么多迭代......但由于我的编码方式可能正在消耗更多的内存......

更新:有人指出我这个问题太大了。对不起,我会尝试分成小部分。正如我所说,这个问题主要是出于好奇,即使在移动设备中性能似乎也可以忽略不计。感谢您的所有反馈。

4

1 回答 1

15

我认为这是过早的优化。您已经知道在大多数情况下性能不是问题。即使在紧密的循环中,性能也不会降低那么糟糕。让 JavaScript 引擎像 Chrome 开始做的那样通过从闭包中删除不需要的变量来自行优化它。

一件重要的事情是,不要通过不必要的优化使您的代码更难阅读。您的示例需要更多代码,从而阻碍了开发。在某些情况下,我们被迫使代码更难阅读,因为我们知道应用程序的某个特定部分更占用内存/性能,但只有在那时我们才应该这样做。

如果您在以下代码中添加断点(在 Chrome 中),您将看到world变量已在闭包之外进行了优化,请查看范围变量http://jsfiddle.net/4J6JP/下的“闭包”节点1/

在此处输入图像描述.

(function(){
   var hello = "hello", world="world";
    setTimeout(function(){
        debugger;
        console.log(hello);
    });
})()

请注意,如果您在该内部函数中添加 eval,则所有赌注都将关闭,并且无法优化闭包。

于 2013-02-20T18:51:51.973 回答