function foo(a){ var b = 2; // ... function bar(){ // ... } // ... var c = 3; }
foo 的作用域气泡中包含了标识符 a、b、c 和 bar
bar 拥有自己的作用域气泡
全局作用域也有自己的作用域气泡,它只包含了一个标识符 foo
由于 a,b,c,bar 都附属于 foo 的作用域气泡,因此无法从 foo 的外部对它们进行访问。也就是说,无法在全局作用域中对它们进行访问,会导致 ReferenceError。
函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内反复使用及复用
从所写的代码挑出一个任意的字段,然后用函数声明对它进行包装,实际上就是把这些代码“隐藏”起来了。为什么“隐藏”变量和函数是一个有用对技术,看段代码
function doSomething(a){ b = a + doSomethingElse(a*2); console.log(b*3) } function doSomethingElse(a){ return a - 1; } var b; doSomething(2); // 15
变量 b 和函数 doSomethingElse 应该是 doSomething 内部具体实现的“私有”内容。给予外部作用域不仅没有必要,而且可能是危险对,因为它们可能被有意或无意对方式使用,从而导致超出了 doSomething 的使用条件
function doSomething(a){ function doSomethingElse(a){ return a - 1; } var b; b = a + doSomethingElse(a*2); console.log(b*3) } doSomething(2); // 15
现在,b 和 doSomethingElse 都无法从外部访问,只能被 doSomething 所控制。功能性和最终效果都没有受影响,但是设计上将具体内容私有化了
“隐藏”作用域带来的另一个好处,是可以避免同名标识符之间的冲突
function foo() { function bar(a){ i = 3; console.log( a + i ); } for(var i=0; i<10; i++){ bar( i * 2 ); // 糟糕,无限循环了 } } foo()
bar 内部的赋值表达式 i = 3 意外地覆盖了声明在 foo 内部循环中的 i。导致无限循环,因为 i 被固定设置为 3,永远小于 10
function foo() { function bar(a){ var i = 3; console.log( a + i ); } for(var i=0; i<10; i++){ bar( i * 2 ); } } foo()
bar 内部的赋值操作需要声明一个本地变量来使用。这样就可以,根据作用域规则。另外一种方式是不要用 i 这个名字
变量冲突的一个典型例子存在于全局作用域中。当程序加载了多个第三方库时,如果没有妥善处理,就很容易引发冲突,通常会用命名空间和模块管理来解决
命名空间:这些库通常会在全局作用域中声明一个名字足够独特的变量,通常是一个对象,这个对象被用作库的命名空间
模块管理:模块机制,通过依赖管理器的机制将库的标识符显示地导入到另外一个特定的作用域中