0. 前言
2016年马上就要过去了,首先,提前祝大家元旦快乐,在这最后一个工作日里我还在敲着代码,好了,说一下今天我要分享的"闭包",闭包是谁?干什么的?,有什么优点?什么缺点?闭包就是一个包啊!!!-然后...
1. 简介
1. 什么是闭包呢?
外部函数返回的并持有外部变量的内部函数就是闭包。是不是还没懂,那我就换种说法,(闭包就是指有权访问另一个函数作用域中的变量的函数)
2. 用闭包干什么?
用闭包来突破作用域链,让它可以在外面可以使用内部的变量的方法
3. 优点
- 避免污染全局环境
- 实现封装,防止变量跑到外层作用域中,发生命名冲突
- 匿名自执行函数,匿名自执行函数可以减小内存消耗
4. 缺点
数据长期的驻留在内存中,造成了内存的极大浪费,在IE浏览器下,特别容易崩溃。所以闭包特烦恼,用之需谨慎。
点我
没事别BB,有事用图说,首先感谢美女给我画的图,三克油
2. 代码实现
2.1 作用域链
//作用域链:环境对象中定义的变量,会放到作用域中,形成一个链式结构。
var a = 10;
function F() {
var b = 20;
//内部函数:函数内部定义的函数
function N() {
var c = 30;
return a + b + c;
}
return N();
}
//验证N中是否都能使用所有的链上变量
console.log(F());//输出 60
图 1
首先,我们把全局作用域G,我们可以把它当做可以包含一切的宇宙,其中,包含全局变量 (如a) 和函数 (如F) ,每个函数也都会有属于自己的一块私用空间,用以储存一些别的变量 (如b) 和内部函数 (如N)。
2.2 突破作用域链
代码1
var a = 10;
function F() {
var b = 20;
function N(){
return b;
}
return N;
}
var M = F();
console.log(M());//输出 20
代码2
与上面的代码功能一样,只不过实现过程有些区别
var inner;
var a = 10;
function F() {
var b = 20;
function N(){
return b;
}
inner = N;
}
F();
console.log(inner());//输出 20
图 2
N() 仍然可以访问 F() 空间并使用变量b,虽然现在 N() 和 a 处于同一块空间,但 a 不能访问 b。
2.3 闭包的应用
2.3.1. 循环中的闭包
function F() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr[i] = function(){
return i;
};
}
return arr;
}
var str = F();
console.log(str[0](), str[1](), str[2]());//输出 3 3 3
- arr中每一项执行时,都会去上级作用域寻找i,而i在for循环执行结束后就已经变成了3,所以arr中每一项执行的结果都是一样的。
- 闭包本身不保存上一作用域中变量的值,当使用时是去上面的作用域中查找相同名字变量,使用最近那一层作用域中的变量
2.3.2. 解决
function F() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr[i] = (function(x){
return function(){
return x;
};
})(i);
}
return arr;
}
var str = F();
console.log(str[0](), str[1](), str[2]());//输出0 1 2
arr中每次添加新项是都会使得自执行函数执行,并将i作为参量传入了自执行函数,关键点是 function(x){...}(i)中第一个x是函数的形参,是私有变量,与外面的i没有关系,被私有作用域保护起来了,第二个i才是函数中外面的i(也就是说第一个x只是一个迷惑人的量,你改成k也是一样的结果,只不过是把i赋给k而已)这样一来,每次触发自执行函数时,都相当于将当前循环的变量i存储了下来。
2.3.3. getter方法和setter方法
getter和setter使你可以快速获取或设置一个数据。
为了变量的安全,一般我们不允许直接访问变量,通过中间的方法间接的访问
//getter获取
var getNum;
(function(){
var num = 0;
//getter方法
getNum = function(){
return num;
};
})();
console.log(getNum());// 输出 0;
//setter设置
var setNum;
var getNum;
(function(){
var num = 0;
//setter方法
setNum = function(x){
//安全的考虑
if (typeof x === "number") {
num = x;
}
};
//getter方法
getNum = function(){
return num;
};
})();
setNum(100);
console.log(getNum());//输出 100;
2.3.4. 数组的迭代器
var arr = [1,2,3,4,5];
function setUp(x){
var i = 0;
return function(){
if (x.length == i - 1) {
i = 0;
}
return x[i++];
};
}
var next = setUp(arr);
console.log(next());//输出 1
console.log(next());//输出 2
console.log(next());//输出 3
console.log(next());//输出 4
console.log(next());//输出 5