前言
在上一章中提到了作用域的定义与规则,它主宰着JS引擎该如何在作用域或包含它的任意嵌套作用域中查询一个变量,我们也证明了JS的scope是在编译时决定的,而这种範围的术语称为Lexical Scope
。
Lexcal Scope的关键思想就是它完全由function
,block
,变量宣告
的放置位置来控制,如果你的宣告变数放置在function中,则编译器在解析的时候会将该变数与function的範围作连接;若是放置在block(let/const),则它会与最近的封闭block{...}
相连。
必须将变量定义在可以引用的範围之一,否则它会被定义为undefined
,如果在当前作用域中没有找到这个变量则会往下一个(外部)的作用域去寻找,持续找到匹配的变量或是退到全域作用域,若到全域作用域都没有找得则会值出一个错误。
Marbles,and Vuckets,and bubbles
将上一章所使用的程式码来举例,我们将其中的作用域用不同的颜色定义以便区分。
// outer/global scope: REDvar students = [ { id: 14, name: "Kyle" }, { id: 73, name: "Suzy" }, { id: 112, name: "Frank" }, { id: 6, name: "Sarah" }];function getStudentName(studentID) { // function scope: BLUE for (let student of students) { // loop scope: GREEN if (student.id == studentID) { return student.name; } }}var nextStudent = getStudentName(73);console.log(nextStudent); // Suzy
标记了三种颜色以代表三个作用域:
RED : global scopeBLUE : functiongetStudentName(...)
GREEN : for...in
loop
(图片来源 : You Don't Know JavaScript 2nd
Scope的範围是在编译期间根据function/blocks的位置与彼此之间嵌套关係做决定的,每个作用域一定会被包含在一个父层作用域中,绝对不会有一个作用域分别位于两个不同的作用域中。
而绿色作用域几乎在蓝色作用域中,而蓝色作用域也几乎都在红色作用域中,这代表作用域可以相互嵌套并且根据需求嵌套任何深度,你可以使用自身或上层作用域中所宣告的变量但是不能使用下一层作用域的变量。
而当我们要寻找一个变量的时候,若在自身作用域中没有找到那么他就会往上层作用域寻找直到全域作用域,举个例子我们在for...of中使用了students
这个变量,但他并不是在蓝色作用域中宣告个,所以他会往上一层作用域(绿色作用域)中寻找,若也没有便会在往上一层(红色作用域),在这里便可以成功照到students这个变量的宣告。
Lookup Failures
当作用域在不断的往上层寻找变量,直到来到了全域作用域后任然没有找到指定的变量,那么便会掷出一个错误,但是这个错误的处理方式会根据变量的作用或程式的运行模式各有不同。
Undefined Mess
若是变量是source,若是往上寻找发现这是一个没有被宣告的变数,那们JS引擎就会掷出一个ReferenceError;若变量是target并且在严格模式下运行,则也会掷出一个ReferenceError。
在大部分的情况下,JS的错误讯息都会是"Reference Error : XYZ is not defined."
,虽然在英文中not defined
与·undefined
都是未定义的,但是在程式中是完全不一样概念。
Global....What?
若我们在非严格模式下使用了一个为定义的target,则会发生一个奇怪的事情,而这其中最麻烦的便是他会自动在全域
变数中创建一个符合的变数。
function getStudentName() { // assignment to an undeclared variable :( nextStudent = "Suzy";}getStudentName();console.log(nextStudent);// "Suzy" -- oops, an accidental-global variable!
当getStudentName(...)中使用了一个未被定义的target变数,因为在自身作用域中没有找到这个变量的宣告,所以会到上一层寻找(全域),而在全域中也没有找到,那么他会先检查是否是处于严格模式下,若不是JS会自动在全域中创建一个符合的变量。
若是处于严格模式下,当检查到全域作用域中发现没有这个变量,那么便会掷出一个ReferenceError。
参考文献:
you Don't Know JavaScript 2nd