JavaScript
事实上是一门编译语言。在传统编译语言中,一段源代码执行前会经历三个步骤:
分词/词法分析(Tokenizing/Lexing)
var a = 2; // 分解后: var、a、=、2、; // 空格是否会被当做词法单元,取决于空格在这门语言中是否具有意义。
有状态
还是无状态
的方式进行的。解析/语法分析(Parsing)
代码生成
作用域
:引擎的另一个好朋友,负责收集并维护由所有声明的标识符(变量)组成的一系列查询,并实行一套严格的规则,确定当前执行的代码对这些标识符的访问权限。var a = 2;
这段代码是一句声明。但会经过编译器和引擎的处理来进行。编译器在编译过程中的第二步中生成了代码,引擎执行它时,会通过查找变量 a 来判断他是否已声明过。查找的过程由作用域进行协助,但是引擎执行怎样的查找会影响最终的查找结果。
引擎常使用的查询类型为:LHS和RHS
LHS: 赋值操作的目标是谁
RHS: 谁是赋值操作的源头
function foo(a) { var b = a; return a + b; } var c = foo(2); // 对话: 1. 声明 var c 2. 对 c 进行 LHS 3. 对 foo(2) 进行 RHS 4. function foo(a) 期间会进行 a = 2, 对 a 进行 LHS 5. 声明 var b 6. 对 b 进行 LHS 7. 对 a 进行 RHS 8. return a + b; 分别对 a、b 进行 RHS // 答案: 1. 所有的 LHS(一共有3处) 1. c =..; 2. a = 2(隐士变量分配) 3. b = .. 2. 所有的 RHS (一共有4处) 1. foo(2.. 2. = a; 3. a.. 4. .. b
作用域是根据名称查找变量的一套规则。
当一个块或函数嵌套在另一个块或函数中时,就会发生作用域的嵌套。因此在当前作用域中无法找到某个变量时,引擎就会在外层作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止。
// 非严格模式下 function foo(a) { console.log(a + b); } var a = 2; foo(2); // 4 // 严格模式下: function foo(a) { console.log(a + b); } var a = 2; foo(2); // 4
遍历嵌套作用域链的规则:引擎会从当前的执行作用域中开始查找变量,如果找不到就会向上一级中继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找的过程都会停止。
例子:
为什么区分 LHS 与 RHS 是一种重要的事?
因为在变量还未声明(在任何作用域中都无法找到该变量)的情况下,引擎的这两种查询行为是不一样的。
// 非严格模式下: function foo(a) { console.log(a + b); b = a; } foo(2); // 4 // 严格模式下: 'use strict'; function foo(a) { console.log(a + b); b = a; } foo(2); // ReferenceError: b is not defined
上述代码引擎行为:
第一次对 b(.. + b) 进行 RHS 查询时未找到该变量,也就是说,这是一个"未声明" 的变量,因为在任何相关的作用域都无法找到它。
第二次对 b(b = ..) 进行 LHS 查询时,如果在顶层(全局作用域)中也没找到该变量,就会在全局作用域中隐式地创建一个该名称的变量,并将其返回给引擎。
......
第一次对 b(.. + b) 进行 RHS 查询时未找到该变量,也就是说,这是一个"未声明" 的变量,因为在任何相关的作用域都无法找到它,直接抛出 'ReferenceError
'。
......
非严格模式下引擎查找规则:
当引擎执行 RHS 查询在所有嵌套的作用域中找不到所需的变量,引擎就会抛出 ReferenceError
异常。
当引擎执行 LHS 查询时,如果在顶层作用域中也无法找到该变量,全局作用域就会创建一个该名称的变量,并将其返回给引擎(非严格模式下)。
严格模式下引擎查找规则:
ES5 引入了 "严格模式"(use strict)
,在行为上有很多不同,其中一个不同的行为就是严格模式下禁止自动或隐式地创建全局变量。因此在严格模式中引擎执行 LHS 查询失败时,并不会创建一个全局变量,而是直接抛出一个 ReferenceError
。
如果 RHS 找到了一个变量,但尝试对这个变量进行一些不合理的操作时,比如对一个非函数类型的值进行函数调用,或者引用 null
或 undefined
类型的之中属性,那引擎则会抛出另外一种类型的异常 TypeError。
ReferenceError
同作用域判断失败相关,而 TypeError
代表作用域判别成功了,但对结果的操作是非法或不合理的。作用域是根据名称查找变量的一套规则。
引擎常使用的查询类型为:LHS 和 RHS
= 操作符
在调用函数时的形参会导致关联作用的赋值操作。也就是说 foo (a, b, c...), 都会有 a = xxx, b = xxx, c = xxx ...... 的行为。非严格模式下引擎查找规则:
当引擎执行 RHS 查询在所有嵌套的作用域中找不到所需的变量,引擎就会抛出 ReferenceError
异常。
当引擎执行 LHS 查询时,如果在顶层作用域中也无法找到该变量,全局作用域就会创建一个该名称的变量,并将其返回给引擎(非严格模式下)。
严格模式下引擎查找规则:
ES5 引入了 "严格模式"(use strict)
,在行为上有很多不同,其中一个不同的行为就是严格模式下禁止自动或隐式地创建全局变量。因此在严格模式中引擎执行 LHS 查询失败时,并不会创建一个全局变量,而是直接抛出一个 ReferenceError
。
如果 RHS 找到了一个变量,但尝试对这个变量进行一些不合理的操作时,比如对一个非函数类型的值进行函数调用,或者引用 null
或 undefined
类型的之中属性,那引擎则会抛出另外一种类型的异常 TypeError。
ReferenceError
同作用域判断失败相关,而 TypeError
代表作用域判别成功了,但对结果的操作是非法或不合理的。Q:(question)
R:(result)
A:(attention matters)
D:(detail info)
S:(summary)
Ana:(analysis)
T:(tips)
以上就是深度剖析JavaScript作用域从局部到全局一网打尽的详细内容,更多关于JavaScript作用域局部全局的资料请关注其它相关文章!