1 函数作用域
函数作用域的含义:属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
嵌套的作用域中也可以使用,但是外层的作用域访问不到。
最小授权或最小暴露原则。
- 可以隐藏内部实现
- 规避冲突(冲突会导致 变量的值被意外覆盖。)
1.1 函数与函数表达式
函数:function foo(){}
函数表达式:(function foo(){})()
最简单点的区分:看function 关键字出现在声明中的位置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中的第一个词,那么就是一个函数声明,否则就是一个函数表达式。
最重要的区别:名称标识符将会绑定在何处。函数foo被绑定在所在作用域中,可以直接通过foo()来调用它。函数表达式foo被绑定在函数表达式自身的函数中而不是所在作用域中。
1.1.1 匿名函数表达式
函数表达式可以是匿名的,而函数声明不能匿名。
匿名函数表达式书简单快捷,有几个缺点:
- 匿名函数在栈追踪中不会显示出有意义的函数名,使调试困难。
- 如果没有函数名,当函数需要引用自身时只能使用已经过期的arguments.callee引用,比如在递归中。另一个函数需要引用自身的例子,是在事件触发后事件监听器需要解绑自身。
- 匿名函数省略了对于代码可读性/可理解性很重要的函数名。一个描述性的名称可以让代码不言自明。
1.1.2 立即执行函数表达式IIFE
函数名对 IIFE 不是必须的,IIFE最常见的用法是使用一个匿名函数表达式。
这两种形式均可:
(function foo(){ .. })()
(function(){ .. }())
传参:
(function IIFE( global ) {
var a = 3;
console.log( a ); // 3 console.log( global.a ); // 2
})( window );
//将 window 对象的引用传递进去,但将参数命名为 global
两个应用场景:
1、解决 undefined 标识符的默认值被错误覆盖导致的异常
2、倒置代码的运行顺序,将需要运行的函数放在第二位
2 块作用域
- 用 with 从对象中创建出的作用域仅在 with 声明中而非外 部作用域中有效。
- ES3 规范中规定 try/catch 的 catch 分句会创建一个块作 用域,其中声明的变量仅在 catch 内部有效。(但是当同一个作用域中的两个或多个 catch 分句 用同样的标识符名称声明错误变量时,很多静态检查工具还是会发出警告。)
- ES6的let和const可以创建块作用域
3 变量提升
—— JavaScript 代码在执行时是由上到下一行一行执行的吗?
—— 不是。
5.1 词法作用域
包括变量和函数在内的所有声明都会在任何代码被执行前首先被处理。
- 1、先有蛋(声明)后有鸡(赋值)。
- 2、只有声明本身会被提升,而赋值或其他运行逻辑会留在原地。 函数声明会被提升,但是函数表达式却不会被提升。
- 3、具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用。
- 4、函数声明和变量声明都会被提升。是函数会首先被提升,然后才是变量。
- 5、重复的 var 声明会被忽略掉,但出现在后面的函数声明还是可以覆盖前面的。
相关资料
《你不知道的JavaScript 上卷》