Kar*_*yak 7 javascript variables hoisting
我对解释器的理解是它逐行执行程序,我们可以看到即时结果,不像转换代码的编译语言,然后执行它.
我的问题是,在Javascript中,解释器如何知道变量是在程序中的某个地方声明并将其记录为undefined?
考虑下面的程序:
function do_something() {
console.log(bar); // undefined (but in my understanding about an interpreter, it should be throwing error like variable not declared)
var bar = 111;
console.log(bar); // 111
}
Run Code Online (Sandbox Code Playgroud)
被隐含地理解为:
function do_something() {
var bar;
console.log(bar); // undefined
bar = 111;
console.log(bar); // 111
}
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?
Li3*_*357 15
var如果你从表面上想到它,这种" 吊装"的概念就会让人感到困惑.你必须深入研究语言本身的运作方式.JavaScript是ECMAScript的一种实现,是一种解释型语言,这意味着您编写的所有代码都被送入另一个程序,而该程序又解释代码,根据源代码的某些部分调用某些函数.
例如,如果你写:
function foo() {}
Run Code Online (Sandbox Code Playgroud)
解释器一旦遇到你的函数声明,就会调用它自己调用FunctionDeclarationInstantiation的函数来创建函数.解释器不是将JavaScript编译为本机机器代码,而是在读取JavaScript代码的每个部分时执行自己"按需"的C,C++和机器代码.它并不一定意味着逐行,所有解释都意味着不会发生编译到机器代码中.执行机器代码的单独程序会读取您的代码并动态执行该机器代码.
这与var声明提升或任何声明有关,是解释器首先读取所有代码而不执行任何实际代码.它分析代码并将其分成块,称为词法环境.根据ECMAScript 2015语言规范:
8.1词汇环境
甲词法环境是用于定义的相关联的规范类型标识符,以基于对代码的ECMAScript词法嵌套结构中的特定的变量和函数.词汇环境由环境记录和外部词汇环境的可能空引用组成.一般一个词法环境与ECMAScript的的代码一些特定的句法结构相关联,例如一个FunctionDeclaration,一个块语句,或捕捉一个条款TryStatement和一个新的词法环境每一个这样的代码被评估时间被创建.
一环境记录记录了被其相关联的词法环境的范围内产生的标识符的绑定.它被称为Lexical Environment的EnvironmentRecord
在执行任何代码之前,解释器会遍历您的代码,并且对于每个词法结构(例如函数声明,新块等),都会创建一个新的词法环境.在这些词法环境中,环境记录会记录在该环境中声明的所有变量,它们的值以及有关该环境的其他信息.这就是允许JavaScript管理变量范围,变量查找链,this值等的原因.
每个词法环境都与代码领域相关联:
8.2代码领域
在评估之前,所有ECMAScript代码都必须与Realm相关联.从概念上讲,领域由一组内部对象,一个ECMAScript全局环境,在该全局环境范围内加载的所有ECMAScript代码以及其他相关的状态和资源组成.
您编写的每个JavaScript/ECMAScript代码部分在实际执行任何代码之前都与领域相关联.每个领域都包含与领域相关的特定代码部分所使用的内在值,领域的this对象,领域的词汇环境等.
这意味着在执行之前会分析代码的每个词法部分.然后创建一个包含该组代码的所有信息的领域.源代码,执行它需要哪些变量,已声明哪些变量,是什么this等等.在var声明的情况下,当您定义一个类似于此处的函数时,会创建一个领域:
function do_something() {
console.log(bar); // undefined
var bar = 111;
console.log(bar); // 111
}
Run Code Online (Sandbox Code Playgroud)
在这里,FunctionDeclaration创建一个与新领域相关联的新词法环境.创建词法环境时,解释器会分析代码并查找所有声明.然后首先在该词汇环境的开头处理这些声明,因此该函数的"顶部":
13.3.2变量声明
一个
var声明宣称的作用域变量运行执行上下文的VariableEnvironment.Var变量在实例化包含词法环境时创建,并在创建时初始化为undefined.
因此,无论何时实例化(创建)词法环境,都会创建所有var声明,并将其初始化为undefined.这意味着它们在任何代码执行之前被处理,位于词法环境的"顶部":
var bar; //Processed and declared first
console.log(bar);
bar = 111;
console.log(bar);
Run Code Online (Sandbox Code Playgroud)
然后,在分析完所有 JavaScript代码之后,最终执行它.因为声明是先处理的,所以声明(并初始化为undefined)给你undefined.
提升机真的有点用词不当.Hoist意味着声明直接移动到当前词法环境的顶部,而是在执行之前分析代码; 什么都没动.
注意:let并const以同样的方式行事并且也被悬挂但这不起作用:
function do_something() {
console.log(bar); //ReferenceError
let bar = 111;
console.log(bar);
}
Run Code Online (Sandbox Code Playgroud)
这将为您提供一个ReferenceError,用于尝试访问未初始化的变量.即使let和const声明被提升,规范也明确规定在初始化之前不能访问它们,这与var:
13.3.1 Let和Const声明
let和const声明定义了作用于正在运行的执行上下文的LexicalEnvironment的变量.变量是在实例化包含词法环境时创建的,但在评估变量的LexicalBinding之前可能无法以任何方式访问它们.
因此,在正式初始化变量之前,无论是未定义还是任何其他值,都无法访问变量.这意味着你似乎无法像你所说的那样"在声明之前"访问它var.
| 归档时间: |
|
| 查看次数: |
792 次 |
| 最近记录: |