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中的函数、递归和闭包,以及执行环境、变量对象与作用域链的概念。 首先,我们来看JavaScript中定义函数的两种方式:函数声明和函数表达式。函数声明是一种明确的、独立的代码结构,例如`...
作用域链是JavaScript中另一个核心概念,它是由函数创建时的作用域决定的,包含了函数被创建时所有可访问的数据对象。这个链由内部属性[[Scope]]定义,它包含了函数定义时所在作用域的对象集合。当函数执行时,会...
首先,**作用域链**是JavaScript中的一种机制,它定义了变量和函数查找的顺序。当尝试访问一个变量时,JavaScript会首先在当前执行上下文中查找,如果找不到,它会沿着执行上下文的外部引用(outer)向上搜索,直到...
作用域链的头部是最接近当前执行环境的变量对象,即当前正在执行函数的活动对象(AO),而链的尾部则是全局活动对象(globalAO)。当代码试图访问一个变量时,JavaScript引擎会顺着作用域链向上查找,直到找到该变量...
JavaScript中的作用域链是编程中一个至关重要的概念,它决定了变量和函数的可访问性以及在不同作用域内的查找顺序。在深入理解作用域链之前,我们首先要了解什么是执行环境和变量对象。 执行环境,简单来说,就是...
JavaScript中的闭包、匿名函数和作用域链是编程中至关重要的概念,它们是理解JavaScript运行机制的关键。在本文中,我们将深入探讨这三个概念,并通过实际示例来展示它们的运用。 首先,我们来讨论“闭包”。闭包是...
"作用域链和闭包:代码中出现相同的变量,JavaScript引擎是如何选择的?" 标题“作用域链和闭包:代码中出现相同的变量,JavaScript引擎是如何选择的?”中,我们可以看到JavaScript引擎是如何选择相同的变量的。...
此外,闭包是JavaScript中一个重要的概念,它允许函数访问外部函数作用域中的变量。闭包在保持变量引用的同时也阻止了变量所在作用域的销毁。这可能会导致内存泄漏,特别是在全局作用域中创建闭包的情况下。因此,...
**作用域链**是JavaScript中用于决定变量访问的一个机制。每个函数在创建时,都会形成一个作用域链,这个链由当前函数的作用域(包含局部变量)和所有父级作用域(直至全局作用域)组成。作用域链确保了函数能够访问...
JavaScript是Web开发中不可或缺的一部分,它提供了丰富的特性,如作用域、闭包、对象和原型链,这些都是理解和编写高效代码的关键。以下是对这些概念的详细解释: 1. **JavaScript变量作用域** - **函数作用域**:...
- 定义:在JavaScript中,如果一个变量在任何函数外部声明,则该变量处于全局作用域下。这意味着它可以在程序的任何地方被访问。 - 特性: - 全局变量在整个程序运行期间都是可用的。 - 当一个变量被声明但未被...
本文探讨了JavaScript中的变量作用域问题,通过对两种变量的特性从不同角度进行分析和测试,讨论了如何控制变量作用域的有效方法。 变量作用域是JavaScript编程过程中经常遇到的问题之一,对于编程人员来说是一个...
在JavaScript中,闭包(Closure)是一个非常重要的概念,它允许一个函数访问并操作其外部作用域中的变量,即使该函数在其外部作用域之外被调用。这种特性使得闭包成为一种强大的工具,能够实现诸如数据封装、私有...
闭包是理解作用域链的关键概念,它允许内部函数记住其定义时的作用域,即使外部函数已经完成执行。闭包常常用于封装变量和实现私有方法,提高代码的封装性和安全性。 总结一下,JavaScript中的函数不仅是一种数据...
在JavaScript中,每个函数都有自己的作用域,而这些作用域按照特定的顺序组织起来,形成了作用域链。这个链帮助解析器在不同层级的上下文中找到变量。 在提供的代码示例中,我们有一个`window.onload`事件处理器,...
Python 中的变量作用域可以分为四个层次:局部作用域(Local scope)、外部作用域(Enclosing scope)、全局作用域(Global scope)和内置作用域(Built-in scope)。 局部作用域是指当前函数或代码块中的变量作用...
JavaScript中的作用域链是理解变量查找和闭包的关键概念。作用域链主要涉及到函数执行上下文和全局执行上下文中的作用域。以下是对这个主题的详细解释: 首先,每个函数在创建时,都会有一个内部属性[[scope]],它...
在示例代码中,可能会展示如何在不同作用域内创建和使用对象,包括如何在函数内部创建局部对象,如何在类中定义成员对象,以及如何利用闭包保持对变量的访问。通过这些例子,开发者将能更直观地看到作用域规则的实际...
JavaScript的闭包与作用域链密不可分,因此本文可以和JavaScript的作用域链相对照分析,一定可以对JavaScript的闭包和作用域链有更深的理解。 下面我们仍然以createComparisonFunction为例进行闭包的分析。 //step1...