购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.1.1 块级作用域

在讨论let和const的具体表现之前,应该先来看看什么是 块级作用域 。在ES2015 诞生之前,给ECMAScript或JavaScript新手解答困惑时经常会提到一个观点——JavaScript没有块级作用域。而对于初学者来说,首先需要了解什么是作用域。

作用域 (Scope)是ECMAScript编程中非常重要的一个概念,虽然它在同步ECMAScript编程中并不能充分地引起初学者的注意,但在异步编程中,良好的作用域控制技巧则成为了ECMAScript开发者的必备技能。

在ES2015 之前的ECMAScript标准中,原本只有全局作用域和函数作用域,如以下代码。

这里定义了foo函数和bar函数,它们的意图都是为了定义一个名为local的变量。但最终的结果却截然不同。

在foo函数中,我们使用var语句来声明定义了一个local变量。用一种比较容易理解的方式来表达,便是因为函数体内部会形成一个作用域,所以这个变量便被定义到该作用域中,而且在foo函数体内并没有做任何作用域延伸的处理,所以在该函数执行完毕后,这个local变量也随之被销毁,而在外层作用域中则无法访问到该变量。

在Web前端开发领域中,初学者总需要自行踩过一些坑才能学会更多的知识,而其中一个则是与作用域有着极强关系的 循环绑定事件

可以假设在某个页面中,需要对一组按钮进行点击事件的绑定,而因为它们的响应途径或方法都是类似的,所以可以用一个函数或者统一的事件处理器对它们进行事件响应。此时就需要对这组按钮进行循环绑定事件处理,如图 3.1 所示。

图 3.1 循环绑定事件

此时这里的需求是,用户点击按钮组中的按钮时,在输出信号窗中显示用户所点击按钮的标签值(A、B、C 和 D)。以最直接和简单的方法,对这个节点列表(NodeList)进行历遍,对其中每一个节点进行事件绑定。

这段代码看上去貌似并没有什么问题,但是当我们任意点击一个按钮时,程序都会报错,如图 3.2 所示。

图3.2 程序报错

初学者可能会对这个报错感到有些匪夷所思,从逻辑上分析程序并没有什么问题,但为什么会出现错误呢?如果要详细分析其中的出错原因,我们需要先对for循环的运行原理有更深入的理解。

我们假设有一段用于计算数组总和的程序。

在这段代码中,首先定义并初始化了一个用来存储待累加项的数组和一个总和整型变量。接下来,开始进行循环。在该for循环的初始化代码中,我们定义并初始化了两个变量:i(计数器)和len(循环数组长度的别名)。当i小于len时,循环条件成立,执行逻辑代码,每次逻辑代码执行完毕以后,i自增 1。在循环的逻辑代码中,把当前循环的数组项加到总和变量中。这个循环流程图如图 3.3 所示。

图3.3 for循环流程图

从这个流程图中不难发现,程序中真正的循环体不仅有逻辑代码,还包含了实现循环自身的执行判断和循环处理。

在希望通过for循环对按钮组进行事件绑定的代码中,循环体是addEventListener方法的调用,而计数器限定值是节点列表的长度,即按钮的个数。因为在每次逻辑代码执行完毕以后计数器i都会自增 1,而i是从 0 开始计数的,所以在循环完成以后计数器i的值应该为按钮的个数。然而在ECMAScript对数组的定义中,元素下标是从 0 开始的,这意味着一个含有 3 个元素的数组,只有下标为 0、1、2 的元素,而没有下标为 3 的元素。所以在buttons里并没有下标为 4 的元素,故buttons[4]返回的是 undefined。如图 3.4 所示,在使用var来定义的计数器中,i会存在于for循环的上一层作用域中,每一个按钮的事件响应函数在寻找变量i的时候,会一层一层地向上寻找,如图 3.5 所示。

图 3.4 for循环与var变量定义

图3.5 变量寻找路径

而这里我们需要再次提到“ JavaScript 没有块级作用域 ”这句话,因为计数器i存在于上一层作用域中,这就意味着在对于它的引用被全部解除之前,它都不会被垃圾收集器所标记并清除,而一直保持着循环结束后的值。

这就意味着,在每一次用户点击任何一个按钮时,触发的响应函数中对计数器i的引用所获得的值都会是一样的,即按钮的个数,这便是导致前面报错的最直接原因。块状作用域的作用便是希望在for循环所属的{ ... }内能形成一个作用域,即块状作用域,让在循环体内所定义的变量或常量能留在循环体内。 JlkcMo53I4TAB7HNG9lkPolqwCqowiypGD+fWeIwNfoNZoYBQLuKcnLdP51Ed9zD

点击中间区域
呼出菜单
上一章
目录
下一章
×