Javascript - 谨慎使用闭包?

mow*_*ker 21 javascript

我正在观看http://www.youtube.com/watch?v=mHtdZgou0qU,并在大约13:37(嘿嘿),他显示了一个由于添加了一个新对象而要避免的事项列表的幻灯片范围链.

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

Sas*_*gov 20

这是因为,要查找非本地变量,VM必须走向范围链才能找到它们.另一方面,局部变量被缓存,因此局部变量查找要快得多.函数嵌套越多,范围链变得越长,潜在的性能影响就越大.

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

(function(window, document, undefined) {
  // ...
})(window, document);
Run Code Online (Sandbox Code Playgroud)

在这里,windowdocument成为局部变量,因此查找它们会变得更快,如果您在代码中引用这些对象数千次,则会变得非常明显.

该页面对范围链和执行上下文进行了非常深入的描述.(如果你有时间阅读它,整篇文章很有趣.)

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

  • @Walkerneo:如果你没有引用非局部变量,它不是一个闭包.他说的是你应该尽可能避免闭包,但当你*引用超出范围的变量时,你应该使用局部变量来更快地查找(如果你经常在该函数中使用变量 - - 否则它不会做太多). (2认同)

Pet*_*son 9

链接的视频解释了为什么关闭可以从11:08开始造成一些性能命中.

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

要查找与变量关联的值,Javascript解释器将遵循以下过程:

  1. 搜索本地范围对象
  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);
}
Run Code Online (Sandbox Code Playgroud)

从最里面的函数来评估表达式x + y + z,它必须在范围链中遍历三个级别才能找到x,然后它必须再次遍历范围链两个级别才能找到y,然后最后一次找到z.总的来说,它必须搜索范围链中的六个对象以返回最终结果.

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

另请注意,在Javascript中,创建函数(尤其是闭包)会产生很大的开销.以这个相当简单的闭包为例:

function a(x) {
  return function (y) {
    return x + y;
  }
}
Run Code Online (Sandbox Code Playgroud)

你可以在几个不同的时间称呼它,就像这样

var x = a(1);
var y = a(2);
var z = a(3);
alert(x(3)); // 4
alert(y(3)); // 5
alert(z(3)); // 6
Run Code Online (Sandbox Code Playgroud)

您会注意到,返回的函数a必须保留在父函数中已经作为参数传递的内容,即使已经调用了父函数.这意味着解释器必须在内存中保留您传入到目前为止调用的每个函数的内容.