《javascript高级程序设计》中闭包的概念:javascript
闭包,实际上是一种语言特性,它是指的是程序设计语言中,容许将函数看做对象,而后能像在对象中的操做般在函数中定义实例(局部)变量,而这些变量能在函数中保存到函数的实例对象销毁为止,其它代码块能经过某种方式获取这些实例(局部)变量的值并进行应用扩展。html
咱们的理解:java
其实闭包就是一个函数,一个外部函数经过调用函数并return返回出内部函数,这里的内部函数就是一个闭包;此时在内部函数中是能够访问到外部函数的变量的;浏览器
要想理解闭包,首先咱们要了解栈堆内存和做用域链;首先咱们来说解栈堆内存:闭包
首先咱们来看个demo:函数
var a=1; var obj={"name":"咸鱼"}
上面简单的两句代码,其实就是在内存中作了两件事,效果图以下:性能
在js简单实现深浅拷贝(http://www.javashuo.com/article/p-clxgdfpk-gq.html)一文中咱们知道基本数据类型是存储在栈内存中的,引用数据类型是存储在堆内存中的,其实上面的两句代码在内存中就是作了两件事:1.首先在栈内存中开辟了一块空间用来存放a的变量和值;2.在堆内存中开辟了一块空间用来存储obj的值,同时在将地址指向栈内存中的变量名objspa
若是咱们在代码下面再加上一句obj={"name":'张三"},这个时候咱们以前存储name为咸鱼的值也就是obj原来的值会被js中的垃圾回收机制回收掉,而后obj的值从新的指向{name:"张三"}这个值;设计
做用域链code
再来看一下这个例子:
var a = 1; function fn(){ var b = 2; function fn1(){ console.log(b);//2 console.log(a);//1 } fn1(); } fn();
效果图以下:
1.var a=1;这个时候咱们是在全局执行环境的,浏览器的全局环境就是window做用域,咱们的window做用域中有a和fn;
2.当咱们往下走到fn的时候,栈内存会开辟一块新的执行环境,此时fn的执行环境中咱们有b和fn1;
3.当咱们接着往下走到fn1的时候,这时栈内存一样会开辟一块新的执行环境,此时fn1的执行环境中是没有任何变量数据的,可是咱们在fn1中输出a、b,咱们都是能够读取到的;这是由于程序在读取变量的时候是从内到外的开始读的,是随着fn1开始往上一层一层的查找,是这样的执行顺序(fn1 = > fn = > window),若是找到window中尚未读取到变量,这时程序才会报错;
固然在执行的过程当中,垃圾回收机制若是检测到程序执行完了是会进行垃圾回收的,避免形成内存泄露等问题;就是说咱们的fn1里面执行完以后fn1的做用域就会被销毁,接着程序执行fn,fn执行完以后fn就会被销毁;往上执行到全局的时候,整个程序就没有了fn的做用域和fn1的做用域,只剩下浏览器的全局做用域window,这个时候window里只剩a和fn;
了解了上面的做用域链和栈内存和堆内存的知识以后,咱们来开始讲解js闭包:
function outer() { var a = '123' return function add(){
//在这里由于做用域的关系,add是能访问到outer的全部变量的,可是outer是访问不到add的变量;
//因此思路一转,把add的值做为结果return出来变通实现outer外部函数访问到了内部函数变量
// add就是一个闭包函数,由于他可以访问到outer函数的做用域,add中没有找到变量a,则会继续往上层做用域找 console.log(a); } } var inner = outer() // 得到add闭包函数 inner() //"123"
首先咱们能够看到,在全局做用域下咱们是有一个outer函数的,outer做用域里面有a和add,add做用域里面执行控制台输出a的变量,此时这里的add函数就造成了一个闭包,由于add函数里面须要访问到outer做用域下的a变量,而他们不处在同一个做用域中,因此二者相互牵引,须要输出a,上面outer中的变量a就必须得在,做用域链查找到outer的时候找到a了,输出a的时候,垃圾回收机制会认为add尚未执行完成,由于此时的做用域链查找已经到了outer做用域下,因此不会清理a的内存空间;因此这就会带来一个问题:若是咱们屡次的使用闭包,则会给咱们的程序带来内存占用过多,致使性能问题;
函数内部能访问全局变量是javascript语言的特殊之处,可是若是咱们想达到函数外部能访问内部变量的时候,咱们就可使用闭包,这就是闭包给咱们带来的便利;
闭包的优缺点:
优势:
1.能够读取函数内部的变量
2.能够避免全局污染
缺点:
1.闭包会致使变量不会被垃圾回收机制所清除,会大量消耗内存;
2.不恰当的使用闭包可能会形成内存泄漏的问题;
总结:
1.做用域链查找变量的方式是一层一层的往上查找,直到找到为止,若是找到window全局做用域还未找到,就报undefined;
2.嵌套函数中,由于不在同一做用域,正常状况下内外部函数是访问不到内部函数的,可是经过闭包能够实现;
3.尽量少的使用闭包,由于会形成内存消耗大以及有可能形成内存泄露(若是不须要的时候,不要随便使用);