什么时候使用闭包?
想重用一个变量,但是又想保护变量不被篡改 (本质:给一个函数保护一个专属的变量,仅 函数自己可反复使用,别人无法使用 )
如何使用闭包?
- 用外层函数包裹内层函数 和要保护的变量
- 外层函数将内层函数return到外层函数内部
- 外部的使用者,需要调用外层函数,获得返回的内层函数对象,并将内层函数对象保存在变 量中 反复使用
闭包形成的原因?
外层函数调用后,外层函数的作用域对象被内层函数的作用域链引用着无法释放,成为内层函 数的私有财产,就形成了闭包
function parent(){ //外层函数 --- 妈妈
var total = 1000; //要保护的变量 ---红包
return function (money){ //内层函数 --- 小孩
total-=money;
console.log(剩余:total)
}
}
var pay = parent(); ---妈妈生孩子 每调用一次函数创建一个红包(要保护的变量)
pay(100);
pay(100); //反复调用 时和妈妈再无关系 (闭包保护变量的方式)
pay(100);
标准闭包情况:(生一个孩子的情况)
生两个孩子的情况:
因为每调用一次外层函数 就会创建一个保护的变量
所以第二次 var pay2 的时候就会重新创建一个保护的变量 var total = 1000
所以 调用pay2(10) 的返回值为990
笔试题:
function fun (){ //外层函数
for( var i = 0; arr=[]; i<3; i++){ //var i =0是循环变量 循环执行了三次 0 1 2因为i<3
arr[i] = function(){ console.log(i) } //内层函数 孩子,目前仅创建了孩子没有调用
} //此循环执行了三次 相当于生了三个孩子 结果为 arr[0] = function(){}
arr[1] = function(){}
arr[2] = function(){}
return arr;
}
var funs = fun(); //此时的var funs变量 是fun()函数的return返回值 也就是指的 return的 arr
所以现在的funs就是一个这样的数组 [ function(){} , function(){} , function(){} ]
同上arr[0]...
调用了一次外层函数 就会创建一次函数作用域变量 此时的 i = 3 因为当循环不再能执行的时候
i=3 假设i=2循环可以正常执行 只有i=3的时候才能退出循环 所以在上边return arr;前 i= 3
所以下边孩子共用一个变量 i=3
funs[0]();//3 //funs[0] 就是 数组里的第一个函数function(){} 加()调用
funs[1]();//3 //funs[1] 就是 数组里的第二个函数function(){} 加()调用
funs[2]();//3 //funs[2] 就是 数组里的第三个函数function(){} 加()调用
注意:外层函数 向外边抛出几个内层函数对象 有三种方式
1. return function(){ xxxxx }
2. 还可以通过直接给变量赋值的形式,向外抛出一个函数
(在js中强行给未声明的变量赋值,会自动在全局创建该变量)
3. return一个对象或数组,在对象和数组中包含多个函数定义 (上边的funs笔试题)
判断是否是否属于闭包?有三个条件
1.外层函数包裹要保存的变量 和 使用变量的内层函数
2.外层函数 将 内层函数返回到外部 (上边解释有三种方法将内层函数抛出)
3.调用外层函数 获得内层函数对象 并保存在变量中 反复使用
// 看看是否符合闭包条件 ?
// 1. 外层函数包裹要保存的变量和使用变量的内存函数
function fun (){ //外层包裹的函数
var n = 999; //要保存的变量
nAdd=function(){ n++ }; //内层函数
//(在js中强行给未声明的变量赋值,会自动在全局创建该变量nAdd)
//2. 外层函数将内层函数返回到外部
return function(){ //内层函数
console.log(n)
}
}
//fun()返回(return)什么, getFun就会拿到什么 也就是 function(){xxxx}
var getFun = fun(); //3. 调用外层函数 获得了内存函数的对象,并保存在了变量中反复使用
//因为只调用了一次外层函数 所以创建一个作用域变量 以下三个孩子共用
getFun(); //第一次调用 n输出999
nAdd(); //在调用nAdd()会对n++ 也就+1=1000
getFun(); //因为只对一个作用域变量进行操作 所以在此调用getFun() 输出n 也就为1000
//如果函数外边有个全局变量
反复调用外层函数的情况:
每调用一次外层函数 就会创建一次受保护的局部变量 和函数
function f1 (n){ //外层函数
//形参 n 是 f1 的局部变量,也是受保护的变量
function f2(){ alert(n) };
return f2; //返回内层函数
}
var num = new Array();
for( var i = 0;i < 4; i++){
num[i]=f1(i) //这里进行了赋值 就会有变量提升 function f1()由原来的位置提到前边
//这里 把外层函数 放在了循环里 循环了四次 所以就调用了四次 以下四个生成的函数和闭包互不干扰!
//第一次调用外层函数 i=0 num[0]=f1(0) 所以此时形参n为0 f2(){ alert (0) }
//第二次调用外层函数 i=1 num[1]=f1(1) 所以此时形参n为1 f2(){ alert (1) }
//第三次调用外层函数 i=2 num[2]=f1(2) 所以此时形参n为2 f2(){ alert (2) }
//第四次调用外层函数 i=3 num[3]=f1(3) 所以此时形参n为3 f2(){ alert (3) }
}
//function f1 (n){
// function f2(){ alert(n) };
// return f2;
//}
num[2](); //2
num[1](); //1
num[0](); //0
num[3](); //3