我正在观看http://www.youtube.com/watch?v=mHtdZgou0qU,在大约13:37(呵呵),他展示了一个避免使用的事物列表,因为新对象添加到作用域链上。
我理解他所说的using
和try-catch
语句,以及访问超出作用域的变量,但我不明白为什么应该避免闭包。如果闭包的本地变量将位于作用域链的顶部,那么哪里会有性能损失?
我正在观看http://www.youtube.com/watch?v=mHtdZgou0qU,在大约13:37(呵呵),他展示了一个避免使用的事物列表,因为新对象添加到作用域链上。
我理解他所说的using
和try-catch
语句,以及访问超出作用域的变量,但我不明白为什么应该避免闭包。如果闭包的本地变量将位于作用域链的顶部,那么哪里会有性能损失?
这是因为,为了查找非局部变量,虚拟机必须沿着作用域链向上查找它们。另一方面,局部变量被缓存,所以局部变量的查找速度要快得多。函数嵌套越深,作用域链就越长,可能的性能影响也就越大。
这就是为什么你经常会在流行的JS库中看到像这样的代码:
(function(window, document, undefined) {
// ...
})(window, document);
在这里,“window”和“document”成为本地变量,因此查找它们变得更快,如果您从代码内部引用这些对象数千次,则这一点会变得非常明显。
这个页面详细描述了作用域链和执行上下文。(如果你有时间阅读整篇文章的话,整篇文章也很有趣。)
尽管如此,现代浏览器将所有这些优化到几乎可以忽略的程度,所以这不是我需要担心的事情。
这个视频解释了闭包为什么在大约11:08开始会有一些性能问题。
基本上,他说对于每个嵌套函数,它都会向作用域链中添加另一个对象,因此访问闭包外的变量将需要更长的时间。
要查找与变量关联的值,Javascript 解释器会执行以下过程:
在普通函数中,要查找一个变量,只需要在作用域链的顶部进行搜索。但是,闭包要查找父级变量,则必须向下搜索作用域链,有时要搜索数层深度。例如,如果您有像这样的一些闭包(请注意,这是一个非常牵强附会的例子):
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。总共需要搜索六个对象才能返回最终结果。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
返回的函数必须保留已传递给父函数的参数,即使父函数已经被调用。这意味着解释器必须在内存中保存您到目前为止调用的每个函数中传递的参数。
y
和 z
存储为第三个函数的本地变量是否仍会降低性能?当然,对于这个示例,没有创建闭包的理由,但是如果有呢? - mowwwalker