一、什么是闭包
简而言之,就是能够读取其他函数内部变量的函数。
由于JS变量作用域的特性,外部不能访问内部变量,内部可以外部变量。
二、使用场景
1,实现私有成员。
2,保护命名空间,避免污染全局变量。
3,缓存变量。
一个闭包封装的例子:
复制代码 代码示例:
var person = function () {
// 变量作用域为函数内部,外部无法访问
var name = "default";
return {
getName: function () {
return name;
},
setName: function (newName) {
name = newName;
}
}
}();
console.log(person.name); // 直接访问,结果为:undefined
console.log(person.getName()); // 结果为:default
console.log(person.setName("langjt"));
console.log(person.getName()); // 结果为:langjt
再看循环中常用闭包解决引用外部变量问题:
复制代码 代码示例:
var aLi = document.getElementsByTagName('li');
for (var i=0, len=aLi.length; i<len; i++) {
aLi[i].onclick = function() {
alert(i); // 无论点击哪个<li>元素,弹出的值都为len,表明这里的i和在for之后打印i的值是一样的。
};
}
使用闭包后:
复制代码 代码示例:
var aLi = document.getElementsByTagName('li');
for (var i=0, len=aLi.length; i<len; i++) {
aLi[i].onclick = (function(i) {
return function() {
alert(i); // 此时点击<li>元素,就会弹出对应的下标了。
}
})(i);
}
三、注意事项
1. 内存泄漏
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题。
比如:
复制代码 代码示例:
function foo() {
var oDiv = document.getElementById(‘J_DIV’);
var id = oDiv.id;
oDiv.onclick = function() {
// alert(oDiv.id); 这里存在循环引用,IE低版本页面关闭后oDiv仍在内存中。所以尽可能缓存基本类型而不是对象。
alert(id);
};
oDiv = null;
}
2. 变量命名
如果内部函数的变量和外部函数的变量名相同时,那么内部函数再也无法指向外部函数那个同名的变量。
比如:
复制代码 代码示例:
function foo(num) {
return function(num) {
console.log(num);
}
}
var f = new foo(9);
f(); // undefined
其实上面的用法,专业术语叫函数柯里化(Currying),就是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。本质上也利用了闭包可以缓存的特性,比如:
复制代码 代码示例:
var adder = function(num) {
return function(y) {
return num+y;
};
};
var inc = adder(1);
var dec = adder(-1);
//inc, dec现在是两个新的函数,作用是将传入的参数值 (+/