我知道 JavaScript 是被解释的,而不是被编译的。没有问题。但是,我一直在这里读到 JavaScript 是“动态”执行的,并且一次读取一行。当涉及到以下示例时,这个想法让我很困惑:
writeToConsole();
function writeToConsole() {
console.log("This line was reached.");
}
为了记录,这段代码将很好地写入控制台。尽管如此,浏览器如何知道
exampleFunction()
的存在?如果还没有达到这个功能呢?换句话说,这个函数是什么时候第一次被解释的?
最佳答案
首先,您做出了错误的假设:编译了现代 JavaScript。引擎如 V8 , SpiderMonkey , 和 Nitro 将 JS 源码编译成原生 machine code主机平台的。
即使在较旧的引擎中,也不会解释 JavaScript。他们将源代码转换为 bytecode , 引擎的 virtual machine执行。
这实际上是 Java 和 .NET 语言中的工作方式:当您“编译”应用程序时,实际上是将源代码转换为平台的字节码,Java bytecode和 CIL分别。然后在运行时,一个 JIT compiler将字节码编译成机器码。
实际上只有非常古老和简单的 JS 引擎 interpret JavaScript 源代码,因为解释很慢。
那么JS编译是如何工作的呢?在第一阶段,源文本被转换为 abstract syntax tree (AST) ,一种数据结构,以机器可以处理的格式表示您的代码。从概念上讲,这很像 HTML 文本如何转换为其 DOM表示,这是您的代码实际使用的内容。
为了生成 AST,引擎必须处理原始字节的输入。这通常由 lexical analyzer 完成.词法分析器并没有真正“逐行”读取文件;而是逐字节读取,使用语言的语法规则将源文本转换为标记。然后词法分析器将 token 流传递给 parser ,这就是实际构建 AST 的原因。解析器验证 token 形成有效序列。
您现在应该能够清楚地看到为什么语法错误会阻止您的代码工作。如果源文本中出现意外字符,引擎将无法生成完整的 AST,也无法进入下一阶段。
一旦引擎有了 AST:
因此,您现在应该至少可以看到,JS 执行分两个阶段进行。
但是,执行阶段实际上对您的示例工作的原因没有影响。它的工作原理是 rules定义如何评估和执行 JavaScript 程序。规则可以很容易地以一种方式编写,使您的示例不起作用,而不会影响引擎本身实际解释/编译源代码的方式。
具体来说,JavaScript 有 a feature俗称吊装。为了理解提升,您必须了解函数声明和函数表达式之间的区别。
简单地说,函数声明是当你声明一个将在别处调用的新函数时:
function foo() {
}
函数表达式是当您使用
function
时任何需要表达式的地方的关键字,例如变量赋值或参数:var foo = function() { };
$.get('/something', function() { /* callback */ });
JavaScript 要求在执行上下文开始时将函数声明(第一种类型)分配给变量名,不管 声明出现在源文本(上下文)中的位置。执行上下文大致等同于作用域——简单来说,就是函数内部的代码,或者如果不在函数内部,则是脚本的最顶部。
这可能会导致非常奇怪的行为:
var foo = function() { console.log('bar'); };
function foo() { console.log('baz'); }
foo();
您希望将什么记录到控制台?如果你只是线性地阅读代码,你可能会认为
baz
.但是,它实际上会记录 bar
,因为foo
的声明被提升到赋值给 foo
的表达式之上.所以总结一下:
关于javascript - 浏览器是真正逐行读取 JavaScript 还是多次通过?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15395347/