Javascript-谨慎使用闭包

Javascript - Use closures sparingly?

本文关键字:闭包 Javascript-      更新时间:2023-09-26

我在看http://www.youtube.com/watch?v=mHtdZgou0qU,在13:37左右(呵呵),他展示了一张幻灯片,其中列出了由于在作用域链上添加了一个新对象而需要避免的事情。

我理解他对usingtry-catch语句以及访问范围外变量所说的话,但我不明白为什么应该避免闭包。如果闭包的局部变量将位于作用域链的顶部,那么性能损失在哪里?

这是因为,要查找非本地变量,VM必须沿着作用域链查找它们。另一方面,本地变量是缓存的,因此本地变量查找要快得多。函数嵌套越多,作用域链就越长,潜在的性能影响就越大。

这就是为什么你经常在流行的JS库中看到这样的代码的部分原因:

(function(window, document, undefined) {
  // ...
})(window, document);

在这里,windowdocument成为局部变量,因此查找它们的速度会快得多,如果您在代码中引用这些对象数千次,这一点就会变得非常明显。

本页对作用域链和执行上下文进行了非常深入的描述。(如果你有时间读的话,整篇文章都很有趣。)

不过,总的来说,现代浏览器会将所有这些东西优化到基本上可以忽略不计的程度,所以我不会担心。

链接的视频解释了为什么从11:08开始关闭会造成一些性能打击。

基本上,他说,对于每个嵌套函数,它都会将另一个对象添加到作用域链中,因此访问闭包之外的变量将花费更长的时间。

为了找到与变量相关的值,Javascript解释器遵循以下过程:

  1. 搜索本地scope对象
  2. 如果1不起作用,则搜索父作用域对象
  3. 如果2不起作用,则搜索父对象的父作用域对象
  4. 继续搜索父作用域,直到
  5. 您搜索全局范围
  6. 如果仍然找不到,则抛出一个未定义的变量错误

在正常函数中,要查找变量,只需在作用域链的顶部进行搜索。另一方面,为了找到父变量,闭包必须沿着作用域链向下搜索,有时要搜索好几个级别。例如,如果您有这样的闭包(请注意,这是一个非常做作的例子):

function a (x) {
  function b (y) {
    return (function (z) {
      return x + y + z;
    })(y + y);
  }
  return b(x + 3);
}

从最里面的函数开始,要计算表达式x + y + z,它必须在作用域链中向上遍历三个级别才能找到x,然后必须再次在作用域链条中向上平移两个级别才能发现y,最后一次才能找到z。总共,它必须搜索作用域链条上的六个对象才能返回最终结果。

这在闭包中是不可避免的,因为闭包总是必须访问父变量。如果他们不这样做,那么使用闭包就没有任何意义。

还要注意,在Javascript中,在创建函数(尤其是闭包)时会有很大的开销。举个例子,这个相当简单的闭包:

function a(x) {
  return function (y) {
    return x + y;
  }
}

你可以把它称为几个不同的时间,比如这个

var x = a(1);
var y = a(2);
var z = a(3);
alert(x(3)); // 4
alert(y(3)); // 5
alert(z(3)); // 6

您会注意到,从a返回的函数必须保留在父函数中作为参数传递的内容,即使在父函数已经被调用之后也是如此。这意味着解释器必须将您传递给迄今为止调用的每个函数的内容保存在内存中。