声明:部份内容参考文章 Ice-shou:闭包详解一javascript
如下是三本比较权威的书对闭包的解释java
《JavaScript高级程序设计》bash
闭包是指有权访问另外一个函数做用域中的变量的函数;闭包
《JavaScript权威指南》异步
从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。函数
《你不知道的JavaScript》post
当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。性能
说明ui
要搞懂什么是闭包,必需要先搞明白如下几点基础知识spa
JavaScript变量有两种:全局变量,局部变量
局部变量做用域通常在函数里面,在函数以外的视为全局变量
通常来讲,在函数里面能够访问全局的变量,在函数外面不能够访问函数里面的变量
Javascript存在“链式做用域”结构(chain scope),这里的链式做用域能够理解为函数嵌套,子对象会一级一级地向上寻找全部父对象的变量。因此,父对象的全部变量,对子对象都是可见的,反之则不成立.
var str1="hello";//全局变量
function fun(){
var res="i am coming";//fun()内的局部变量
}
function funa(){
var str2=" world";//funa()内的局部变量
function funb(){
function func(){
console.log(str1);//hello
console.log(str2);//world
console.log(res);//报错,undefined
}
func();
}
funb();
}
funa();
console.log(str2);//报错
复制代码
函数表达式调用法
var exc=function(){
console.log("hello world");
}
exc();
复制代码
自调用
(function(){
console.log("hello world");
})();
复制代码
逐层调用
function fun1(){
var a=0;
console.log(a);
return function(){
a++;
console.log(a);
}
}
//注意这种调用的结果:
fun1();
fun1()();
fun1()();
输出
0
0
1
0
1
//解释:每一次先执行fun1(),a都会初始化为0,再执行匿名函数,a++获得1
复制代码
先赋值给一个变量再由变量调用
function fun1(){
var a=0;
console.log(a);
return function(){
a++;
console.log(a);
}
}
//注意这种调用的结果:
var res=fun1();
res();
res();
res();
输出
0
1
2
3
//解释:fun1()只执行一次,因此a=0只执行一次,之后每次执行res()是在执行匿名函数,每执行一次,a自增一次
复制代码
闭包是指有权访问另外一个函数做用域中的变量的函数;《JavaScript高级程序设计》
从技术的角度讲,全部的JavaScript函数都是闭包:它们都是对象,它们都关联到做用域链。
《JavaScript权威指南》
function fn1() {
var str = 'hello,world';
function fn2() {
console.log(str);//能够访问fn1()函数
}
fn2();
}
fn1();
复制代码
说明
对于上面两本书对闭包的定义都比较迷,按照定义,fn2()函数就是一个闭包,,可是这就是闭包了吗?实际上并不明显,咱们看一个更加明显的例子...
function fn1() {
var str = 'hello world';
function fn2() {
console.log(str);
}
return fn2;
}
var fn3 = fn1();
fn3();
复制代码
说明
fn2的词法做用域能访问fn1的做用域
将fn2当作一个值返回
fn1执行后,将fn2的引用赋值给fn3
执行fn3,输出了变量str
当函数能够记住并访问所在的词法做用域时,就产生了闭包,即便函数是在当前词法做用域以外执行。
《你不知道的JavaScript》
function fun1(){
var a=0;
console.log(a);
return function(){
a++;
console.log(a);
}
}
//注意这种调用的结果:
var res=fun1();
res();
res();
res();
输出
0
1
2
3
复制代码
说明
第一次执行var res=fun1(),把匿名函数的引用传给了变量res
每次执行res()即在调用匿名函数,注意调用的位置是在匿名函数做用域以外
通常来讲局部变量在函数执行以后就会别垃圾回收机制回收,可是调用res()以后变量a并无被回收,每执行一次res(),a的值自增一次
res()函数能够记住并访问原来所在的词法做用域
优势
实现了能够访问其余做用域变量,而且避免了全局变量对自身词法做用域变量的污染
能够把局部变量(自身做用域的变量)驻留在内存中一直保存着上一次执行的值,不会被垃圾回收机制回收,从而避免使用全局变量
缺点
上面我用了很多的例子来解释什么是闭包,不难发现闭包存在的形式,就是一个知足闭包定义的各类条件的函数,并且常以匿名函数的形式出现(注意:并非匿名函数都是闭包,两者不能等同)
function fun1(){
return function(){
//闭包主体
}
}
var res=fun1();
res();//闭包函数调用
复制代码
(function(i){
//闭包主体
})(i);//闭包函数自调用
复制代码
异步程序中避免因执行时间不一致致使变量丢失
//打印1-10
for (var i = 1; i <= 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}
//结果打印了10个11
复制代码
缘由说明
解决
上例很明显就是在匿名函数里面访问全局变量,因为异步缘由致使并未能准确打印出全局变量的值,因此解决方案就是循环i的时候,把i保存在私有做用域中而且一直保存,使用闭包来实现
for (var i = 1; i <= 10; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000);
})(i);
}
复制代码