JS基础(二)块作用域
欢迎阅读,本篇主要介绍JS中的块作用域.
前言
尽管上篇讲到的函数是最常见的作用域单元,本质上,声明在一个函数内部的变量或函数会在所处的作用域中“隐藏”起来,这是有意为之的良好软件设计原则。但函数不是唯一的作用域单元,其他类型作用域是存在的,通过使用其他类型的作用域单元甚至可以实现维护起来更加优秀、简洁的代码。
什么是块作用域?
块作用域指的是变量和函数不仅可以属于所处的作用域,也可以属于某个代码块,通常指 {}
内部。
for () { }
1 |
|
相信大家对上述代码非常熟悉,我们在for循环的头部直接定义变量 i,通常是因为只想在for循环内部的上下文中使用 i,而忽略了 i 会绑定在外部作用域(函数或全局)中的事实。这就是块级作用域的用处。变量的声明应该距离使用的地方越近越好,并最大限度地本地化。块作用域是一个用来对之前 最小授权
原则进行扩展的工具,将代码从函数中隐藏信息扩展为在块中隐藏信息。
with 关键字
with 关键字也是块级作用域的一种形式,用 with 从对象中创建的作用域仅在 with 声明中而非外部作用域中有效,由于 with 关键字并不常用且不推荐使用,再此不做详细讨论。
try/catch
很少有人注意到JS的ES3规范中规定 try / catch 的 catch 分句会创建一个块级作用域,其中声明的变量仅在 catch 内部有效。
1 |
|
尽管这个行为已经被标准化了,而且被大部分的标准JS环境所支持,但是当同一作用域中的两个或多个 catch 分句用同样的标识符名称声明时,很多静态检查工具(并不是全部)还是会发出警告,实际上这并不是重复定义,因为所有变量都被安全地限制在块作用域内部,为了避免不必要的警告,我们一般会将 catch 的参数命名为 err1、err2 等,或者关掉检查工具。
let
ES6 引入了 let 关键字,提供了除了 var 之外的另一种变量声明方式。let 关键字可以将变量绑定到所在的任意作用域中(通常是 {…}内部)。或者说,let 为其声明的变量隐式的“劫持”了所在的块作用域。
1 |
|
用 let 将变量附加在一个已经存在的块级作用域上的行为是隐式的。在开发中,如果我们没有特意关注哪些作用域中绑定了哪些变量,并且习惯性移动或将这些块包含在其他块里,就会导致代码变得混乱。为作用域显示的创建块可以部分解决这个问题,使代码的附属关系变得更加清晰,通常来讲,显式的代码优于隐式或一些精巧但不清晰的代码。
1 |
|
只要是声明有效,在声明中的任意位置都可以使用 {…} 括号来为 let 创建一个用于绑定的块,我们在 if 声明内部显式的创建了一个块,如果需要对其进行重构,整个块都可以被方便地移动而不会对外部 if 声明的位置和语义产生任何影响。
需要说明的是,在我们的项目中,显示创建块级作用域({…})需要和团队成员在写法、分块原则、代码易读性上达成共识后再使用,使用不当,反而会让代码变得难读懂、难维护,所以你可能会看到有人反而不推荐此种写法。
const
除了 let 之外,ES6 还引入了 const,同样可以用来创建块作用域变量,其值是固定的常量。之后任何试图修改值的操作都会引起错误。
1 |
|
总结
从ES3开始,try / catch 结构在 catch 分句中具有块作用域。
从ES6中引入了 let 关键字用来在任意代码块中声明变量。if () { let a = 2; } 会声明一个劫持了 if 的 {…} 块的变量,并将变量添加到这个块中。
块级作用域不应该完全作为函数作用域的代替方案,两种功能应该同时存在,开发者可以并且应该根据需要选择使用哪种作用域,创造可读、可维护的优良代码。
下篇计划学习 - 提升。感谢一起学习。