以前一直对闭包这个概念模模糊糊的,网上也是长篇大论,如今经过本身了解和学习,总结了一下一些闭包知识点,写得不对的地方能够指出,你们互相学习.面试
先了解一些变量的做用域:markdown
变量的做用域包括两种:全局变量和局部变量。闭包
全局变量:异步
var n = 999;//全局变量
function f1(){
console.log(n);
}
f1();//999
复制代码
局部变量:函数
function f1(){
var n = 999;//局部变量
}
console.log(n);//n is not defined
复制代码
先看一下MDN关于闭包的定义:学习
一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一块儿(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你能够在一个内层函数中访问到其外层函数的做用域。在 JavaScript 中,每当建立一个函数,闭包就会在函数建立的同时被建立出来。ui
重点的一句:闭包让你能够在一个内层函数中访问到其外层函数的做用域。spa
如今不理解也不要紧,继续往下看:code
学习一个概念时,最好的方法就是找它的demo,从demo中理解和分析,下面先看一段代码,这是一个最简单的闭包:orm
function f1(){
var n = 999;
function f2(){
console.log(n);
}
return f2//返回内部函数f2,这样在f1中就能读取f2的数据和函数等价于window.f2 = f2;
}
var result = f1();
result();//999
复制代码
在上边的代码中,f1函数里面嵌套了一个函数f2,而且f2调用了f1的变量,那么变量n和函数f2组合就成了一个闭包。
那为何是闭包呢?咱们能够根据上边MDN对闭包的定义这句话(闭包让你能够在一个内层函数中访问到其外层函数的做用域。)进行分析,咱们再看一张图:
f1是一个外部函数,变量n是外部函数的局部变量,f2是嵌套在f1中的一个内部函数,在内部函数f2中调用了外部函数f1的变量n,因此f2和变量n就组成了一个闭包。
那么,咱们就能够得出产生闭包的条件:
只要知足以上两个条件,就产生了闭包。
那你可能会问为何要return f1呢?
由于在JS中,只要内部函数才可以读取外部函数的内部变量或数据,反之则不行,若是你不return f2,那你将没法使用f2这个闭包,return f2是为了在f1中能使用f2的变量和数据,与闭包没有关系的。
那到底什么是闭包呢?
能够通俗理解成:闭包就是有权访问另外一个函数做用域中内部变量或数据的函数,由于在JS中,只要内部函数能可以读取外部函数的变量或数据,反之就不行,全部能够将闭包简单理解成,定义在一个函数内部的函数。
总结:
闭包就是有权访问另外一个函数内部变量的函数。
闭包产生的缘由:内部函数存在对外部函数局部变量的引用就会致使闭包。
到这里相信你也已经对闭包有了一个简单的了解了,可是单单是了解仍是不够的,咱们学学习同样技术,最重要的就是要学以至用,那咱们继续往下了解吧。
最大的一个用途就是前面提到的能够:读取内部函数的变量;
function f1(){
var n = 999;
function f2(){
console.log(n);
}
return f2;
}
var result = f1();
result();//999
复制代码
var n = 999;
function f1(){
var n = 1000;
function f2(){
console.log(n);
}
return f2
}
function f3(p){
var n = 1001;
p();
}
f3(f1());//1000
复制代码
var n = 999;
(function f1(){
console.log(n);
})()
//999
复制代码
上边的代码中f1( )是一个闭包,调用了全局变量n(即调用了window下的变量n);
for(var i = 0; i<10; i++){
(function(j){
setTimeout(function(){
console.log(j);
},1000)
})(i)
}
//1,2,3,4,5,6,7,8,9,10依次打印
复制代码
window.n = 999;
setTimeout(function f1(){
console.log(window.n);
},1000)
复制代码
能够看下下面这段代码:
function f1(){
var n = 999;
function f2(){
console.log(n++);
}
result f2
}
var result = f1();
result();//1000
复制代码
上边代码中f1的内部变量n一直存在内存中,不会在f1调用结束后被自动清除。 再看另外一段代码:
function f1(){
var n = 999;
nAdd = function(){
n+=1;
}
function f2(){
console.log(n);
}
result f2
}
var result = f1();
result();//999
nAdd();
result();//1000
复制代码
上边代码中函数f1的返回值赋值给了全局变量result,函数f1的返回值实际上就是f2函数,能够理解为f2被赋值给了全局变量result,这就致使了f2始终在内存中,而f2的存在依赖于f1,所以f1也始终在内存中,不会在调用结束以后,被垃圾回收机制(GC机制)回收,全部很容易形成内存泄漏。
内存泄漏,就是一些你访问不到或用不到的变量,还占据着内存空间,不能被再次利用起来。
var Counter = (function(){
var privateCounter = 0;
return function changeBy(val){
privateCounter += val;
}
return {
increment:function(){
changeBy(1);
},
decrement:function(){
changeBy(-1);
},
value:function(){
return privateCounter;
}
}
})();
console.log(Counter.value());//0
Counter.increment();
Counter.increment();
console.log(Counter.value());//2
Counter.decrement();
console.log(Counter.value());//1
复制代码
由于使用闭包会包含其余函数的做用域,会比其余函数占据更多的内存空间,不会在调用结束以后被垃圾回收机制(简称GC机制)回收,多度使用闭包会过分占用内存,形成内存泄漏。
一、简述什么是闭包,闭包的做用是什么?写出一个简单的闭包例子。
二、闭包会形成内存泄漏吗?
会,由于使用闭包会包含其余函数的做用域,会比其余函数占据更多的内存空间,不会在调用结束以后被垃圾回收机制回收,多度使用闭包会过分占用内存,形成内存泄漏。
三、for循环和闭包(必刷题)
var data = [];
for(var i = 0; i < 3; i++){
data[i] = function (){
console.log(i);
};
}
data[0]();//3
data[1]();//3
data[2]();//3
复制代码
上边代码的变量i属于一个全局变量,公用一个做用域,全部输出是3个3; 使用闭包改善上边的写法达到预期的效果:
var data = [];
for(var i = 0; i < 3; i++){
(function(j){
setTimeout(data[j] = function(){
console.log(j);
},0)
})(i)
}
data[0]();
data[1]();
data[2]();
复制代码
四、请写出如下代码的输出结果:
第一题4-1:
var n = 10;
function f1(){
var n = 20;
function f2(){
n++
console.log(n);
}
f2();
return f2
}
var result = f1();//21
result();//22
result();//23
console.log(n);//10
复制代码
第二题4-2:
function makeAdder(x){
return function(y){
return x+y;
}
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2));//7
console.log(add10(2));//12
复制代码
第三题4-3:
var Counter = (function(){
var privateCounter = 0;
return function changeBy(val){
privateCounter += val;
}
return {
increment:function(){
changeBy(1);
},
decrement:function(){
changeBy(-1);
},
value:function(){
return privateCounter;
}
}
})();
console.log(Counter.value());//0
Counter.increment();
Counter.increment();
console.log(Counter.value());//2
Counter.decrement();
console.log(Counter.value());//1
复制代码
第四题4-4:
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); //0
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); //2
Counter1.decrement();
console.log(Counter1.value()); //1
console.log(Counter2.value()); //0
复制代码
Counter1和Counter2是两个独立的闭包,一个闭包变量的值改变不会影响到另外一个闭包的变量。
第五题4-5:
for(var i = 0; i < 10; i++){
setTimeout(
function(){
console.log(i);
},1000)
}
//10 10 10 10 10 10 10 10 10 10每隔1秒输出10,一共10个10
复制代码
由于setTimeout是异步的,for循环是同步的,同步代码执行完,i已是10了,异步代码才开始执行,因此i最后打印的是10。
若是将var换成let,打印的结果也不同:
for(let i = 0; i < 10; i++){
setTimeout(
function(){
console.log(i);
},1000)
}//1,2,3,4,5,6,7,8,9,10
复制代码
在for循环中使用var,那i就是一个全局变量,循环结束以后i的值为10。
持续补充更新,记录很差的地方望指出修改,共同进步~
天天都给本身打气,今天也要加油鸭~