这是我关于“函数与块作用域”的注释,这是凯尔·辛普森(Kyle Simpson)撰写的《 您不知道Javascript:作用域和闭包 》的第三章。
功能范围
函数创建作用域。
例如:
函数foo(a){
var b = 2;
//一些代码
功能bar(){
// ...
}
//更多代码
var c = 3;
}
a
, b
, c
和bar()
标识符都属于foo
的范围。 它们也可用于bar
的嵌套范围,除非bar
的范围中有阴影变量。 您无法在全局范围内访问foo
的标识符。 console.log(a);
抛出ReferenceError
。
隐藏在普通范围内
避免标识符被覆盖或彼此冲突的一种有用技术是将范围包装在另一个范围中,例如将一个功能包装在另一个功能中。
这种设计的原因在于“最低权限原则”(又称“最低权限”)或“最少曝光”的背后。它表示仅公开必要的内容,并在代码中隐藏其他所有内容。
这是一个很好的例子:
函数doSomething(a){
函数doSomethingElse(a){
返回-1;
}
var b;
b = a + doSomethingElse(a * 2);
console.log(b * 3);
}
doSomething(2); // 15
doSomethingElse
是doSomethingElse
的功能,因此它正确嵌套在doSomething
。
如果必须在范围的不同层中使用具有相同名称的标识符,请确保使用var
声明它们,否则它们可能会发生冲突,被覆盖并导致不良结果。
“依赖关系管理器”是模块中使用的工具,可以帮助防止冲突范围,但是如果您进行防御性编码,则无需这些就可以达到相同的结果。
由于变量冲突通常在全局范围内发生,因此您可以执行许多库的操作并创建一个通常用于对象的唯一名称,并以该唯一名称对象的属性和方法的形式编写所有功能。
作用域
有时,只要一次,函数标识符就可以“污染”全局范围。 在这些情况下,立即调用函数表达式(IIFE)很好用。 IIFE仅受其自身范围的约束。
命名函数是一个好习惯。 匿名函数(未命名的函数)具有三个缺点。
- 匿名函数在堆栈跟踪中没有名称,这使得调试变得很困难。
- 当您需要为诸如递归之类的东西自引用函数时比较困难。 不推荐使用
arguments.callee
参考。 - 描述性名称有助于自文档代码。
内联函数表达式很有用。
作为范围的块
块范围指的是变量和函数可以属于任意代码块而不是封闭函数的想法。
从ES3开始, try/catch
结构在catch
子句中具有块作用域。
在ES6中,引入了let
关键字( var
关键字的一个表亲)以允许声明变量并将变量附加到代码块的范围内。 例如,在if (..) { let a = 2; }
if (..) { let a = 2; }
, a
成为if
的{..}
块范围的一部分。
const
作用相同,除了值在首次声明后无法更改。
编写显式的代码块有助于保持代码的可读性,并且在以后需要重构代码时也可以提供帮助。 这是一个例子:
var foo = true;
如果(foo){
{// <-显式块的开始
让bar = foo * 2;
bar =某物(bar);
console.log(bar);
} // <-显式块的结尾
}
console.log(bar); // ReferenceError
将不需要保留的标识符放在代码块中有助于“垃圾回收”,或者让大内存标识符被回收并将内存重新分配给其他对象。
应该使用功能范围和块范围来获得更好的,更具可读性/可维护性的代码。