闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?

先说标识符识别性能

标识符识别不是免费的,事实上没有哪种电脑操作可以不产生性能开销。

    在运行期上下文的作用域链中,一个标识符所处的位置越深,它的读写速度就越慢

    所以,函数中局部变量的访问速度总是最快的,而全局变量通常是最慢的(优化的 JavaScript 引擎在某些情况下可以改变这种状况)。

    请记住,全局变量总是处于运行期上下文作用域链的最后一个位置,所以总是最远才能触及的。图 2-4 和 2-5 显示了作用域链上不同深度标识符的识别速度,深度为 1 表示一个局部变量。

闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?_第1张图片

图 2-4 写操作的标识符识别速度

闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?_第2张图片

图 2-5 读操作的标识符识别速度

    总的趋势是,对所有浏览器来说,一个标识符所处的位置越深,读写它的速度就越慢。采用优化的 JavaScript 引擎的浏览器,如 Safari 4,访问域外标识符时没有这种性能损失,而 Internet Explorer,Safari 3.2,和其他浏览器则有较大幅度的影响。值得注意的是,早期浏览器如 Internet Explorer 6 和 Firefox 2,有令人难以置信的陡峭斜坡,如果此图包含它们的数据,曲线高点将超出图表边界。

    通过以上信息,在没有优化 JavaScript 引擎的浏览器中,最好尽可能使用局部变量。一个好的经验法则是:用局部变量存储本地范围之外的变量值,如果它们在函数中的使用多于一次

闭包,作用域,和内存

闭包是 JavaScript 最强大的一个方面,它允许函数访问局部范围之外的数据。

    闭包的使用通过 Douglas Crockford 的著作流行起来,当今在最复杂的网页应用中无处不在。不过,有一种性能影响与闭包有关。

为了解与闭包有关的性能问题,考虑下面的例子:

function assignEvents() {
    var id = "xdi9592";
    document.getElementByIdx("save-btn").onclick = function(event) {
        saveDocument(id);
    };
}

assignEvents()函数为一个 DOM 元素指定了一个事件处理句柄。此事件处理句柄是一个闭包,当 assignEvents()执行时创建,可以访问其范围内部的 id 变量。用这种方法封闭对 id 变量的访问,必须创建一个特定的作用域链。

    当 assignEvents()被执行时,一个激活对象被创建,并包含了一些应有的内容,其中包括 id 变量。它将成为运行期上下文作用域链上的第一个对象,全局对象是第二个。当闭包创建时,[Scope]属性与这些对象一起被初始化(见图 2-7)。

闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?_第3张图片

图 2-7 assignEvents()运行期上下文的作用域链和闭包

    由于闭包的 [Scope] 属性包含与运行期上下文作用域链相同的对象引用,会产生副作用。通常,一个函数的激活对象与运行期上下文一同销毁。当涉及闭包时,激活对象就无法销毁了,因为引用仍然存在于闭包的[Scope]属性中。这意味着脚本中的闭包与非闭包函数相比,需要更多内存开销。在大型网页应用中,这可能是个问题,尤其在 Internet Explorer 中更被关注。IE 使用非本地 JavaScript 对象实现 DOM 对象,闭包可能导致内存泄露

    当闭包被执行时,一个运行期上下文将被创建,它的作用域链与[Scope]中引用的两个相同的作用域链同时被初始化,然后一个新的激活对象为闭包自身被创建(参见图 2-8)。

闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?_第4张图片

图 2-8 闭包运行

    注意闭包中使用的两个标识符,id 和 saveDocument,存在于作用域链第一个对象之后的位置上。这是闭包最主要的性能关注点:你经常访问一些范围之外的标识符,每次访问都导致一些性能损失

    在脚本中最好是小心地使用闭包,内存和运行速度都值得被关注。但是,你可以通过本章早先讨论过的关于域外变量的处理建议,减轻对运行速度的影响:将常用的域外变量存入局部变量中,然后直接访问局部变量

总结一张图

闭包会牺牲多少性能?它是如何产生内存消耗及性能消耗的?_第5张图片

总结一张图

你可能感兴趣的:(javascript)