定义变量的3个关键字——var、let与const,还可以通过与词法环境 的关系将其进行分类(换句话说,按照作用域分类):可以将var分为 一组,let与const分为一组
-
当使用关键字var时,该变量是在距离最近的函数内部或是在全局 词法环境中定义的。(注意:忽略块级作用域)
function a () { for (var i = 0; i<10; i++) { var b = i }; console.log(b, i) // 9, 10 这里var定义的变量在for循环外也可以访问 }
-
let和const直接在最近的词法环境中定义变量 (可以是在块级作用域内、循环内、函数内或全局环境内)
function a () { for (let i = 0; i<10; i++) { let b = i }; console.log(b, i) // 报错 这里let定义的变量在for循环外不可以访问 }
词法环境是JavaScript作用域的内部实现机制,人们通常称为作用域(scopes)。通常来说,词法环境与特定的JavaScript代码结构关联,既可以是一 个函数、一段代码片段,也可以是try-catch语句。这些代码结构(函 数、代码片段、try-catch)可以具有独立的标识符映射表。
###在词法环境中注册标识符
- 如果是创建一个函数环境,那么创建形参及函数参数的默认 值。如果是非函数环境,将跳过此步骤
- 如果是创建全局或函数环境,就扫描当前代码进行函数声明 (不会扫描其他函数的函数体),但是不会扫描函数表达式或箭头函 数。对于所找到的函数声明,将创建函数,并绑定到当前环境与函数名 相同的标识符上。若该标识符已经存在,那么该标识符的值将被重写。 如果是块级作用域,将跳过此步骤。
- 扫描当前代码进行变量声明。在函数或全局环境中,找到所有 当前函数以及其他函数之外通过var声明的变量,并找到所有在其他函 数或代码块之外通过let或const定义的变量。在块级环境中,仅查找当前 块中通过let或const定义的变量。对于所查找到的变量,若该标识符不存 在,进行注册并将其初始化为undefined。若该标识符已经存在,将保留 其值
我们甚至可以在函数定义之前访问函数。我们可以这么做的原因是 fun是通过函数声明进行定义的,第二阶段(如本章前文所述)表明函 数已通过函数声明进行定义,在当前词法环境创建时已在其他代码执行 之前注册了函数标识符。所以,在执行函数调用之前,fun函数已经存在。
需要注意的是,这种情况仅针对函数声明有效。函数表达式与箭头 函数都不在此过程中,而是在程序执行过程中执行定义的。