JavaScript入门教程

JavaScript简介
JavaScript语法基础
JavaScript流程控制
JavaScript函数
面向对象编程
JavaScript事件
JavaScript DOM
正则表达式
JavaScript BOM
AJAX

专题分析

浏览器兼容性
JS优化
Web前端开发规范
编辑器推荐
总结和笔记

学习助手

对象参考手册
ECMAScript分析
数据中心
QQ交流群

JS闭包应该注意的问题

闭包造成的内存泄漏

在不同的 JavaScript 解释器实现中,由于解释器本身的缺陷,使用闭包可能造成内存泄漏,内存泄漏是比较严重的问题,会严重影响浏览器的响应速度,降低用户体验,甚至会造成浏览器无响应等现象。

JavaScript 的解释器都具备垃圾回收机制,一般采用的是引用计数的形式,如果一个对象的引用计数为零,则垃圾回收机制会将其回收,这个过程是自动的。但是,有了闭包的概念之后,这个过程就变得复杂起来了,在闭包中,因为局部的变量可能在将来的某些时刻需要被使用,因此垃圾回收机制不会处理这些被外部引用到的局部变量,而如果出现循环引用,即对象 A 引用 B,B 引用 C,而 C 又引用到 A,这样的情况使得垃圾回收机制得出其引用计数不为零的结论,从而造成内存泄漏。

上下文的引用

关于 this 我们之前已经做过讨论,它表示对调用对象的引用,而在闭包中,最容易出现错误的地方是误用了 this。在前端 JavaScript 开发中,一个常见的错误是错将 this 类比为其他的外部局部变量:
$(function(){
    var con = $("div#panel");
    this.id = "content";
    con.click(function(){
        alert(this.id);//panel
    });
});
此处的 alert(this.id)到底引用着什么值呢?很多开发者可能会根据闭包的概念,做出错误的判断:
    content
理由是,this.id 显示的被赋值为 content,而在 click 回调中,形成的闭包会引用到 this.id,因此返回值为 content。然而事实上,这个 alert 会弹出”panel”,究其原因,就是此处的this,虽然闭包可以引用局部变量,但是涉及到 this 的时候,情况就有些微妙了,因为调用对象的存在,使得当闭包被调用时(当这个 panel 的 click 事件发生时),此处的 this 引用的是 con 这个 jQuery 对象。而匿名函数中的 this.id = “content”是对匿名函数本身做的操作。两个 this 引用的并非同一个对象。

如果想要在事件处理函数中访问这个值,我们必须做一些改变:
$(function(){
    var con = $("div#panel");
    this.id = "content";
    var self = this;
    con.click(function(){
        alert(self.id);//content
    });
});

这样,我们在事件处理函数中保存的是外部的一个局部变量 self 的引用,而并非 this。这种技巧在实际应用中多有应用,我们在后边的章节里进行详细讨论。关于闭包的更多内容,我们将在第九章详细讨论,包括讨论其他命令式语言中的“闭包”,闭包在实际项目中的应用等等。