javascript中的闭包是一个强大而灵活的武器,搞清闭包,作用域链的作用机理,能让我更好的将闭包运用在我们的项目中。
先看一个闭包在for循环中经典的应用:
function foo(){
for(var i = 0; i<10; i++){
(function(j){
setTimeout(function(){
console.log( "current i:" + j + "--" + new Date().getSeconds() + "s" );
}, j * 1000);
})(i);
}
}
foo();
上面的代码改自Pro JavaScript Techniques
中用js控制css达到动画效果的部分。动画的高度/透明度是根据索引i的值动态设置的,所以我们需要将这个索引i保存下来。这里就有一个问题,为什么我们写成下面的代码就不能得到正确的索引呢?
function foo(){
for(var i = 0; i<10; i++){
setTimeout(function(){
console.log("current i:"+i+"--"+new Date().getSeconds()+"s"); //其实这里也是一个闭包
},i*1000);
}
}
foo();
上面的代码得到的i始终是10,而不是想要的1,2,3...
现在我们来逐步详细分析原因。
1. 进入foo的execution context阶段;
这时创建foo的Variable Object (VO)/Activation Object (AO)
VO(foo) = {
i: undefined,
};
2. foo代码执行阶段
将fooExecutionContext push进Execution Context Stack 中,
i随着循环被修改为相应的数值。
executionContextStack.push(fooExecutionContext);
executionContextStack = [
<foo> functionContext,
globalContext
]
由于在for循环执行的时候,setTimeout内部的匿名函数的execution context对于foo来说是不可见的,因为这时的匿名函数并没有执行,
不能访问、修改该匿名函数内部的变量,所以匿名函数中的i不会被修改为for循环的当前索引。
但是该匿名函数的Variable Object (VO)/Activation Object (AO)已经创建,并且保存了i的引用
。
for循环结束时i值为10,
正是由于匿名函数的VO/AO保存了i的引用,foo运行结束时,Garbage Collector不会销毁foo的VO/AO(上面保存着i=10),所以当setTimeout内的匿名函数运行时,i的值始终为10。
搞清楚了上面的问题后,现在我们用图来解释开始的例子
图 1
上面的图是foo在执行最后一次循环时的运行机理。
图 2
图2是setTimeout内部匿名函数执行时的机理,其中红颜色框起来部分随每个
setTimeout内部匿名函数的不同而不同。
通过上面两副图我们可以清楚的看到,增加的匿名自执行函数的作用就是将for循环的索引作为自己的局部变量保存起来,这样setTimeout里面的匿名函数就可以通过scope chain访问到正确的索引值了。
参考:
http://dmitrysoshnikov.com/ecmascript/javascript-the-core/
http://dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts/
http://dmitrysoshnikov.com/ecmascript/chapter-2-variable-object/
http://dmitrysoshnikov.com/ecmascript/chapter-4-scope-chain/
- 大小: 49.2 KB
- 大小: 34.3 KB
分享到:
相关推荐
本篇文章对Javascript中函数、递归与闭包(执行环境、变量对象与作用域链)的使用进行了详细的分析介绍。需要的朋友参考下
JS三座大山同步异步同步异步区别作用域、闭包函数作用域链块作用域闭包闭包解决用var导致下标错误的问题投票机闭包两个面试题原型、原型链原型对象原型链完整原型链图 JS三座大山 同步异步同步异步区别作用域、...
夯实基础中篇-图解作用域链和闭包.doc
主要介绍了JavaScript作用域、闭包、对象与原型链,结合实例形式总结分析了javascript中变量与函数的作用域、闭包、对象、原形链相关概念、用法及注意事项,需要的朋友可以参考下
本文主要介绍了图解Javascript——作用域、作用域链、闭包等知识。具有很好的参考价值。下面跟着小编一起来看下吧
JavaScript的闭包与作用域链密不可分,因此本文可以和JavaScript的作用域链相对照分析,一定可以对JavaScript的闭包和作用域链有更深的理解。 下面我们仍然以createComparisonFunction为例进行闭包的分析。 //step1...
正如闭包的定义一样:“闭包指的是有权访问另一个函数作用域中的变量的函数”, 闭包最大的意义就在于闭包可以对另一个函数作用域的变量进行访问,由此,闭包可以延伸出一系列的用法。 模仿块级作用域 JavaScript...
前端面试题,包含JavaScript的闭包,作用域,原型,原型链,上下文环境以及DOM,BOM封装函数深度克隆,以及一些常见的·JS问题,试题简单但是容易混淆,作为前端工程师必考题
本文实例讲述了Python 闭包,函数分隔作用域,nonlocal声明非局部变量操作。分享给大家供大家参考,具体如下: 实例对象也可以实现闭包的功能,不过实例对象消耗的资源(内存)比闭包多。 demo.py(闭包): # 闭包,...
JavaScript中出现了一个以前没学过的概念——闭包。何为闭包?从表面理解即封闭的包,与作用域有关。所以,说闭包以前先说说作用域
深入理解javascript原型和闭包(01)——一切都是对象 深入理解javascript原型和闭包(02)——函数和对象的关系
scope-chains-closures, Javascript作用域链和闭包 workshop 范围链和闭包 workshop正在启动$ npm install -g scope-chains-closures$ scope-chains-closures # or, shorter: sccjs使用箭头
深度探讨javascript函数的原型链和闭包
NULL 博文链接:https://xieyaxiong.iteye.com/blog/1558277
如果在一个内部函数里,对一个外部作用域(但不是全局作用域)的变量进行引用,那么内部函数就被称为闭包(closure),而这个被内部函数引用的变量则被成为自由变量 闭包和函数调用没多少相关,而是关于使用定义在...
我自己总结的一点javascript闭包的作用域,可能不是太好
闭包与作用域链是 JavaScript 区别于其它语言的重要特性之一。 作用域JavaScript 中有两种作用域:函数作用域和全局作用域。 在一个函数中声明的变量以及该函数的参数享有同一个作用域,即函数作用域。一个简单的...