一、产生闭包的条件
1.调用的函数是父级作用域内部声明的
2.调用的函数是在父级作用域之外进行调用
3.调用的函数内部使用了父级作用域的内部变量
二、闭包的实例
1、
var a="global variable";
var F=function(){
var b="local variable";
var N=function() {
var c="inner local";
return b;
};
return N;
}
alert(b); //ReferenceError b is not defined
函数F中包含了局部变量b,因此b在全局空间里是不可见的。函数N有自己的私有空间,同时也可以访问F()的空间和全局空间,所以b对他来说是可见的。
2、
var inner;
var F=function(){
var b="local variable";
var N=function() {
return b;
};
inner=N;
}
inner(); // "local variable"
的我们在F()中定义了一个新的函数N(),并且将它赋值给了全局变量inner。由于N()是在F()内部定义的,它可以访问F()的作用域,所以即使该函数后来升级成了全局函数,但它依然可以保留对F()作用域的访问权。
3、
function F(param){
var N=function(){
return param;
};
param++;
return N;
}
var inner=F(123);
inner(); //124
当我们的返回函数被调用时,param++已经执行过一次递增操作了。所以inner()返回的是跟新后的值。由此我们可以看出,函数所绑定的是作用域本身,而不是在函数定义时该作用域中的变量或变量当前所返回的值。
如果一个函数会在其父级函数返回之后留住对父级作用域的链接的话,相关闭包就会被创建起来。但其实每个函数本身就是一个闭包,因为每个函数至少有访问全局作用域的权限,而全局作用域是不会被破坏的。
4、循环中的闭包
function F(){
var arr=[],i;
for(i=0;i<3;i++){
arr[i]=function(){
return i;
};
}
return arr;
}
var arr=F();
arr[0]();
arr[1]();
arr[2]();
以上三个调用输出的结果都是3。
这里我们创建了三个闭包,而他们都指向了一个共同的局部变量i。但是,闭包不会记录他们的值,他们所拥有的只是相关领域在创建时的一个连接(即引用)。
如何纠正这种行为呢?答案是换一种闭包形式:
function F() {
var arr=[],i;
for(i=0;i<3;i++){
arr[i]=(function(x){
return function(){
return x;
}
}(i));
}
return arr;
}
arr[0](); //0
arr[1]();//1
arr[2]();//2
在这里,我们不再直接创建一个返回i的函数了,而是将i传递给了另一个即时函数。在该函数中,i就被赋值给了局部变量x,这样一来,每次迭代中的x就会拥有各自不同的值了。
我们也可以定义一个"正常点的"内部函数(不适用即时函数)来实现相同的功能。要点是在每次迭代操作中,我们要在中间函数内将i的值"本地化"。
function F(){
function binder(x){
return function(){
return x;
};
}
var arr=[],i;
for(i=0;i<3;i++){
arr[i]=binder(i);
}
return arr;
}
5
作为函数返回结构的一部分
var fun = function() {6、赋给外部变量
var x = 0;
return {
fn: function() {
alert(++x);
}
};
}
var r = fun();
r.fn();
r.fn();
var fun1;
var fun2 = function() {
var x = 0;
fun1 = function() {
alert(x);
}
}
fun2();
fun1();
7、异步操作,绑定到dom事件
var fun2 = function() {因为用户点击时触发事件,不是在fun2中内部调用的。
var btn = document.querySelector("#myBtn");
var x;
btn.onclick = function() {
alert(x);
}
}
fun2();
8、
异步操作,setTimeout或setInterval
var fun = function(x) {
setInterval(function() {
console.log(x++);
}, 3000)
}
fun(1);
9、
异步操作,ajax请求回调
10、变量提升
var Person = function() {
this.sayName = function() {
alert(name);
}
var name = "laoyao";
}
var p = new Person();
p.sayName();
尽管name是在sayName之后声明的。这里并不影响闭包的
11、
var fun1 = function() { var x = 1; return function fun2() { var y = 5; return function fun3() { alert("x=" + (++x) +";y=" +(++y)); } } } var fun2 = fun1(); alert("fun2:"+fun2); /* * function fun2() { var y = 5; return function fun3() { alert("x=" + (++x) +";y=" +(++y)); } } * */ var fun6 = fun2(); /* * function fun3() { alert("x=" + (++x) +";y=" +(++y)); } * * */ alert("fun6:"+fun6); fun6(); //执行函数fun3 fun6();
三、闭包的应用
3.1 js中的getter和setter
var getValue,setValue;
(function(){
var secret=0;
getValue=function(){
return secret;
}
setValue=function(v){
if(typeof v==="number"){
secret=v;
}
}
}());
getValue(); //0
setValue(123);
getValue(); //123
setValue(false);
getValue(); //123
在这里,所有一切都是通过一个即时函数来实现的,我们在其中定义了全局函数 setValue()和getValue(),并以此来确保局部变量secret的不可直接访问性。
3.2、迭代器
function setup(x){
var i=0;
return function(){
return x[i++];
}
}
var next=setup(['a','b','c']);
next();// 'a'
next(); // 'b'
next(); // 'c'