什么是闭包:
函数和对其周围状态(词法环境--词法作用域)的引用捆绑在一起构成闭包;这个环境包含了闭包创建时所能访问到的所有局部变量,这个概念非常重要!
闭包的环境是独立的、互不干扰的;结合上面的概念,可以得知,指的是所能访问到的所有的局部变量;
闭包是函数和其所在的词法作用域的组合;词法作用域包含了它所能访问到的所有局部变量;
在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;
闭包的作用:
- 主要是使局部变量常驻内存;普通函数的局部变量在函数执行完毕后,会被垃圾回收机制回收!
- 使从函数外部访问函数内的局部变量成为可能
闭包的缺点:
- 会造成内存泄漏,有一块内存被占用,而不被释放;
- 闭包会在父函数外部,改变父函数内部变量的值;所以,如果把父函数当作对象使用,把内部变量当作私有属性,把闭包当作公有方法,可以在父函数外部通过访问公有方法改变私有变量的值;一定要注意,不要随意改变内部变量的值;(联想Java中,私有属性一般只是提供访问,而不能更改的;所以JavaScript中,用这种方法模拟对象,也一定不要改变私有属性的值!)
词法作用域,若闭包引用的是全局变量,那么这个变量被同类闭包(由同一个函数创建的闭包)共享,若闭包引用的变量是局部变量,那么闭包对于这个变量的引用是独立的、互不影响的;
各个闭包所处环境是独立的、互不干扰的;每当函数被调用时,若函数的引用地址不同,都会重新创建一个新的地址,也就是重新生成了一个闭包;
在JavaScript中,每当函数被创建时,就会在函数生成时生成闭包;
理论知识需要实践来验证!
从最简单的函数开始
function fn(){
var num = 0;
console.log(++num);
}//函数被创建时,在函数生成时生成了闭包;
fn();//创建了一个闭包并且调用了函数,引用的num是局部变量,属于闭包环境中的变量,而各个闭包的环境是独立的、互不影响的;
fn();//创建了一个闭包并且调用了函数,但是两个闭包引用的变量num是处于局部作用域下的,所以是独立的、互不影响;输出如下:1,1;
var num = 0;
function fn(){
console.log(++num);
}
fn();//创建了一个闭包并调用
fn();//创建了一个闭包并调用,但是两个闭包引用的变量num是处于全局作用域下的
//(也就是这个变量并不是处于闭包环境下的,闭包通过作用域链从全局作用域访问到),所以变量共享,输出如下:1,2;
嵌套函数中的闭包
function father(){ var num = 0; function son(){ console.log(++num); } return son; } var fn1 = father();//创建了一个闭包,fn1指向的是内部函数son, var fn2 = father();//创建了另一个闭包,fn2指向的是内部函数son fn1();//引用的变量num是处于闭包环境下的,所以互不干扰,(闭包环境包括了其能访问到的所有局部变量,也就是包含了变量num)
fn2();//输出如下:1,1
fn1();//输出2
var num = 0;
function father(){
function son(){
console.log(++num);
}
return son;
}
var fn1 = father();//创建了一个闭包(生了一个函数),fn1指向的是内部函数son
var fn2 = father();//创建了一个闭包,fn2指向的是内部函数son
fn1();//引用的变量num是处于全局作用域下的,所以变量共享
fn2();//输出如下:1,2;
个人总结:闭包就是函数,其对于变量的访问,与普通函数对变量的访问规则是一样的,首先都是通过作用域链查找变量,如果变量是处于词法作用域中的全局作用域下,所以该变量会被所有闭包共享;
各个闭包的环境是独立的、互不影响的;这句话什么意思呢?环境指的是函数所处的词法环境,包含了所有能访问到的局部变量,不包含全局变量哦,配合这个理解,就很好明白所有函数都是闭包这个概念了,
掌握执行函数时,便创建了一个闭包并调用的概念,也就是说两次执行函数,生成了两个闭包,它们对于函数内部变量的作用是互不干扰的,也就是说闭包的所处环境是独立的,互不干扰的(理解为对于局部变量的引用是互不干扰的);可以从上面第一和第三个例子验证!
上文重复强调了函数的词法作用域,也就是闭包环境,仅仅是包括了函数所能访问到的所有局部变量!!
每个闭包环境都是独立互不干扰的;
闭包中可以通过作用域链访问到闭包环境外的变量---也就是全局作用域下的变量,该变量是共享的;
闭包的应用:
- 用闭包模拟私有方法;在Java中,是支持将方法声明为私有的,私有方法只能被同一类中的其他方法访问;
var counter = function(){ var privaterNum = 18; function changeBy(val){ privateNum += val; } return { increment: function(){ changeBy(1); }, decrement: function(){ changeBy(-1); },
value: function(){
return privateNum;
} } }();//立即执行函数中,返回了一个闭包,闭包内的词法环境被三个函数共享 counter.increment();//privateNum == 19
console.log(counter.value());//192、在循环中创建闭包
for(var i = 0; i < 4; i++){ setTimeout(function(){ console.log(i); },1000) } //直接输出四个4,在闭包环境中找不到i变量,沿着作用域链访问到全局变量i,在定时器回调输出时,i已经变成了4 for(var i = 0; i < 4; i++){ (function(i){ setTimeout(function(){ console.log(i); },1000*i) })(i); } //输出0,1,2,3每隔一秒输出一个;定时器回调输出i,在闭包环境中找到了各自的局部变量i,(局部变量是不会受到其他作用域的影响的);
3、函数工厂
function makeSizer(size){ return function(){ document.body.style.fontSize = size + 'px } } var size12 = makeSizer(12); var size14 = makeSizer(14);//函数工厂生成函数,