JavaScript闭包原理与用法实例分析(2)

闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

function f1(){ var n = 999; nAdd = function(){n += 1} function f2(){ alert(n); } return f2; } var result=f1(); result();//999 nAdd(); result();//1000

result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制回收。

2) 让函数内部的变量的值始终保持在内存中(延长局部变量的寿命)。

var print, add, set; function closure() { var number = 8; print = function() { alert(number); } add = function() { number++; } set = function(x) { number = x; } } closure();//创建一个闭包 add(); print();//9 set(0); print();//0 var oldClosure = print; closure();//创建一个新的闭包 print();//8 oldClosure();//0

使用闭包的注意点:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。也就是说,闭包会引用外部函数作用域,会占用更多的内存,过度使用闭包,会导致性能问题。所以,仅当必要时才使用闭包。对产生闭包的函数,使用后应该解除引用。

3)自执行函数+闭包减少全局变量污染(封装私有变量)

var person = (function() { var_name = "Alice"; var _id = 16; return { getUserInfo: function() { return _name + ": " + _id; } } })();

使用下划线来约定私有变量_name和_age,它们被封装在闭包产生的作用域中,外部是访问不到这两个变量的,这就避免了对全局的命令污染。

④ 闭包的缺点:

1) 需要维护额外的作用域。

2) 过渡使用闭包会占用大量内存。

4、this对象

在闭包内使用this对象将产生一些复杂的行为。this对象的值基于函数所在的执行环境在运行时决定:在全局函数中使用时,this等于window(非严格模式)或undefined(严格模式);而当作为对象的方法调用时,this等于这个对象。

var name = "The window"; var object = { name: "My object", getName: function() { return function() { return this.name; }; } }; alert(object.getName()());//输出:"The window"

每个函数一旦被调用,它将自动获得this和arguments两个变量。一个内部函数是不能直接从外部函数访问到这两个变量的。可以通过将this对象存储在另一个变量中来解决这个问题。把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了。

var name ="The window"; var object = { name: "My object", getName: function() { var that = this; return function() { return that.name; }; } }; alert(object.getName()());//输出:"My object"

要让闭包访问外部函数的this和arguments对象,可以通过将它们的引用存储在另一个变量中来完成。

5、内存泄漏

使用闭包的时候很容易造成循环引用,若闭包的作用域包含着一些DOM节点,这时候就有可能造成内存泄漏,但其实,这本身不是闭包的问题,而是由于:BOM和DOM中的对象是使用C++以COM对象的方式实现的,而COM对象的垃圾收集机制采用的是引用计数策略,在基于引用计数策略的垃圾回收机制中,若两个对象之间形成了循环引用,则这两个对象都无法被回收。

function assignHandler() { var element = document.getElementById("id"); element.onclick = function() { alert(element.id); } }

匿名函数保存了一个对element的引用,只要匿名函数存在,element的引用数至少为1,它所占用的内存就永远不会被回收。

function assignHandler() { var element = document.getElementById("id"); var id = element.id; element.onclick = function() { alert(id); } element = null; }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/108a8498b2c38600d5d52d324cdc13f0.html