对于闭包的深刻理解
2017-04-05 18:02
148 查看
闭包(closure),在深刻了解闭包之前,要了解以下知识点:
1、全局变量
2、局部变量
3、作用域:指一个变量的作用范围,本质是一个保存变量的对象(可以叫做作用域对象)
4、作用域链:相当于一个规则,指定了引擎寻找变量的一个顺序
5、执行期上下文栈 ECS(执行环境栈:Execution Context Stack)
6、执行期上下文(EC):也就是执行环境
7、活动对象 AO(Active Object)
闭包产生的条件:
1、受保护的变量和操作变量的函数封装在一个外层函数中定义;
2、外层函数,要将内层函数对象返回;
3、使用者调用外层函数,获得内层函数对象。
闭包的形成过程:
1、在程序执行之前:
产生一个ECS(为了顺序放置所执行函数的EC);首先会向栈中压入主程序main,产生一个全局作用域对象window(该对象中保存着全局变量);其中引用的函数对
象有一个scope属性引用着该全局作用域对象(也就是引用函数来自的作用域对象);
2、在外层函数对象调用时:
产生一个局部作用域对象(也就是一个AO,保存着该函数的局部变量);该AO有一个parent属性指向栈前的一个作用域对象;该AO被内层函数对象的scope属性引
用着;而由于是返回了内层函数对象,所以内层函数是被全局作用域对象所引用着的;
引擎会沿着AO 向parent方向找变量(每个作用域对象连接起来的结构就是作用域链),直到找到为止。
3、当外层函数对象调用后:
本来没有被引用的AO 会在函数调用后而释放,然后垃圾回收。但是现在内层函数对象引用着它,且内层函数被全局作用域对象所引用着,而全局作用域对象是不会
释放的,这就直接导致了内层函数对象不会释放,从而使外层函数的AO被留下来,所以在继续执行(被全局作用域对象引用的)内层函数对象时,作用域链上会多了一
个外层函数对象的作用域。引擎也会经过该作用域查找变量。该作用域上的变量也就被保护起来,一直存在,可以被重用,且不会被污染。
一个经典的setTimeout题目:
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i);
},1000);
}
将上述代码修改一下:使得运行结果为0,1,2,3,4
解析: 页面中所有的setTimeout都会放在同一个队列中一次执行的(顺序由设定的延迟时间大小所决定的);这个队列需要等到所有可执行代码执行完毕后,才会开始
执行这个队列(不管延迟时间是否为0);所以上述题目中有5个setTimeout被放在队列中,等到可执行代码结束时,i已经为5,所以输出结果会是5个5;这时我们需要用到
闭包,在每次循环时将i保护起来,所以可以如下形成闭包(在外部,或在第一个函数参数处):
1、 for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000);
})(i)
}
2、 for(var i=0;i<5;i++){
setTimeout(
(function(i){
return
function(){
console.log(i);
}
})(i),1000);
}
可以通过Chrome中的断点查看当运行到内层函数时,形成了闭包将i保护了起来,作用域链上有三个作用域对象。
1、全局变量
2、局部变量
3、作用域:指一个变量的作用范围,本质是一个保存变量的对象(可以叫做作用域对象)
4、作用域链:相当于一个规则,指定了引擎寻找变量的一个顺序
5、执行期上下文栈 ECS(执行环境栈:Execution Context Stack)
6、执行期上下文(EC):也就是执行环境
7、活动对象 AO(Active Object)
闭包产生的条件:
1、受保护的变量和操作变量的函数封装在一个外层函数中定义;
2、外层函数,要将内层函数对象返回;
3、使用者调用外层函数,获得内层函数对象。
闭包的形成过程:
1、在程序执行之前:
产生一个ECS(为了顺序放置所执行函数的EC);首先会向栈中压入主程序main,产生一个全局作用域对象window(该对象中保存着全局变量);其中引用的函数对
象有一个scope属性引用着该全局作用域对象(也就是引用函数来自的作用域对象);
2、在外层函数对象调用时:
产生一个局部作用域对象(也就是一个AO,保存着该函数的局部变量);该AO有一个parent属性指向栈前的一个作用域对象;该AO被内层函数对象的scope属性引
用着;而由于是返回了内层函数对象,所以内层函数是被全局作用域对象所引用着的;
引擎会沿着AO 向parent方向找变量(每个作用域对象连接起来的结构就是作用域链),直到找到为止。
3、当外层函数对象调用后:
本来没有被引用的AO 会在函数调用后而释放,然后垃圾回收。但是现在内层函数对象引用着它,且内层函数被全局作用域对象所引用着,而全局作用域对象是不会
释放的,这就直接导致了内层函数对象不会释放,从而使外层函数的AO被留下来,所以在继续执行(被全局作用域对象引用的)内层函数对象时,作用域链上会多了一
个外层函数对象的作用域。引擎也会经过该作用域查找变量。该作用域上的变量也就被保护起来,一直存在,可以被重用,且不会被污染。
一个经典的setTimeout题目:
for(var i=0;i<5;i++){
setTimeout(function(){
console.log(i);
},1000);
}
将上述代码修改一下:使得运行结果为0,1,2,3,4
解析: 页面中所有的setTimeout都会放在同一个队列中一次执行的(顺序由设定的延迟时间大小所决定的);这个队列需要等到所有可执行代码执行完毕后,才会开始
执行这个队列(不管延迟时间是否为0);所以上述题目中有5个setTimeout被放在队列中,等到可执行代码结束时,i已经为5,所以输出结果会是5个5;这时我们需要用到
闭包,在每次循环时将i保护起来,所以可以如下形成闭包(在外部,或在第一个函数参数处):
1、 for(var i=0;i<5;i++){
(function(i){
setTimeout(function(){
console.log(i);
},1000);
})(i)
}
2、 for(var i=0;i<5;i++){
setTimeout(
(function(i){
return
function(){
console.log(i);
}
})(i),1000);
}
可以通过Chrome中的断点查看当运行到内层函数时,形成了闭包将i保护了起来,作用域链上有三个作用域对象。
相关文章推荐
- 对于JS语言的深刻理解(变量定义;作用域链;闭包;this)
- javascript-对于闭包的理解
- 深刻理解闭包
- 对于闭包的一些理解
- 实践中对于闭包的一些理解
- 对于闭包的理解;有什末特性,对页面有什么影响
- 对于闭包的理解
- JS闭包之深刻理解
- JavaScript中对于闭包的理解
- 深刻理解JavaScript---闭包
- C++对于类访问权限public,protected,private的深刻理解
- 对于闭包的一些理解
- [Android]对于Android:Layout_weight的深刻理解
- 对于c++面向对象的深刻认识和理解--哲学角度看问题(源生论)
- 谈谈我对闭包知识的深刻理解
- 对于排序的深刻理解
- 闭包的深刻理解,解释很详细
- 深刻理解js执行原理和闭包
- 对于 Javascript 闭包理解
- 对于闭包的理解和prototype的应用原理