JavaScript中的闭包
闭包:
在MDN中的解释:闭包是指那些能够访问独立(自由)变量的函数 (变量在本地使用,但定义在一个封闭的作用域中)。换句话说,这些函数可以“记忆”它被创建时候的环境。
简单理解就是:
如果一个函数会在其父级函数返回之后留住对父级作用域的链接的话,相关闭包就会被创建出来。
闭包#1:
var a = "global variable"; var F = function(){ var b = "local variable"; var N = function(){ var c = "inner local"; return b; } return N; } //函数N有自己的私有空间,同时可以访问F()的空间和全局空间,所以b对它来说是可见的,因为F()是一个全局函数,所以可以将它的返回值赋值给 //其他全局变量,生成一个可以访问F()私有空间的新全局函数。 var inner = F(); inner();
闭包#2:
F()不再返回新函数,而是直接在函数体内创建一个新的全局函数inner()。
var inner; var F = function(){ var b = "local variable"; var N = function(){ return b; }; inner = N; } //此时,调用F()函数,则会创建一个新的函数N,并且将它赋值给全局变量inner。 F(); inner();
闭包#3:
该函数返回一个子函数,这个子函数返回的则是复函数的参数:
function F(param){ var N = function(){ return param; } param++; return N; } var inner = F(10); inner();//11
于是我们知道,函数所绑定的是作用域本身,而不是在函数定义时,该作用域中的变量返回的值。
循环中的闭包:
来看一个三次的循环操作,在每次迭代中都会创建一个返回当前序列号的新函数,该函数会被添加到一个数组中并返回。
function F(){ var arr = [],i; for(i=0;i<3;i++){ arr[i]=(function(x){ return function(){ return x; } }(i)); } return arr; } var arr = F(); arr[0](); //或者,不使用即时函数,将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; }
关于闭包的应用:
应用#1:
将私有变量保护起来:
var getValue,setValue; (function(){ var secret = 0; getValue = function(){ return secret; }; setValue = function(v){ if(typeof v === 'number'){ secret = v; } } }()); getValue(); setValue(11); getValue();//11
应用#2:(迭代器)
将下一个封装给易于使用的next()函数。
function setup(x){ var i = 0; return function(){ return x[i++]; } } var next = setup(['a','b','c']); next(); "a" next() "b" next() "c"
应用#3:
function makeSizer(size) { return function() { document.body.style.fontSize = size + 'px'; }; } var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16); document.getElementById('size-12').onclick = size12; document.getElementById('size-14').onclick = size14; document.getElementById('size-16').onclick = size16;
应用#4:
(模块模式,用闭包来定义公共函数,且其可以访问私有函数)
var Counter = (function(){ var privateCounter = 0; function changeBy(val){ privateCounter += val; } return { increment:function(){ changeBy(1); }, decrement:function(){ changeBy(-1); }, value:function(){ return privateCounter; } } })(); console.log(Counter.value()); Counter.increment(); Counter.increment(); console.log(Counter.value()); Counter.decrement(); console.log(Counter.value());