JavaScript中的代码是怎么编译执行的?

对于js编译基础的理解(持续更新)

对于JavaScript的学习:js是一种发展了数十年的语言,学习js的路程感觉并不像是模块化的知识学习,层层嵌套,需要反复学习理解。


毒汤:走弯路是必然的,没有谁可以通过捷径走向成功


前言

现在要探讨的东西有点基础,但是越基础的东西往往越容易让我们理解一些复杂的程序的运行…


提示:以下是本篇文章正文内容,下面案例可供参考

一、首先得理解作用域

示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的。

1. 编译的过程

程序中的代码执行一般分为三个步骤:分词/词法分析解析/语法分析以及代码生成

  • 分词/词法分析
    例如 var a = 2; ,这个过程是对字符串的分解:vara=2;(空格是否会被当做词法单元,取决于空格是否有意义),这些代码块被称为词法单元(token)。
    词法分析这些词法单元的主要差异在于这些 token 的状态,比如a为一个有状态的词法单元,调用有状态的解析规则,则这个过程就是词法分析。

  • 解析/语法分析
    这个过程是将词法单元流转换成了一个由元素逐级嵌套所组成的代表程序语法结构的树。
    这个树被称为抽象语法树(Abstract Syntax Tree,AST)。

  • 代码生成
    将AST转换为可执行代码的过程称为代码生成。
    比如 var a = 2,所执行的代码为:创建一个叫做 a 的变量,并将 2 这个值存储在 a 中。


2. 理解作用域

  • 2.1 引擎
    负责整个JavaScript程序的编译及执行过程。
  • 2.2 编译器
    负责语法分析以及代码生成。
  • 2.3 作用域
    负责收集并维护所有声明的标识符(变量)组成的一系列查询,确定当前执行的代码对这些标识符的访问权限。

举个例子:

    console.log(a);
    var a = 2;

因为变量提升,相当一部分人会认为结果为 2 ,实际上是 undefined
借此来理解一下作用域:
首先引擎会扫描代码,因为变量提升,第一步先执行 var a,这时 a 并没有被赋值,此时执行 console.log(a); 打印的结果是 undefined。然后才会进行 a = 2赋值操作。

实际的执行过程等同于:

	var a;
    console.log(a);	// undefined
    a = 2;

局部变量与全局变量
  • 局部变量:
    1、在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)
    2、局部变量只能在函数内部使用,在局部作用域中可以访问到全局变量。
    3、在函数内部 var 声明的变量就是局部变量;
    4、函数的形参实际上就是局部变量;
  • 全局变量
    1、在全局作用域下声明的变量叫做 全局变量(在函数外部定义的变量)
    2、全局变量在全局(代码的任何位置)下都可以使用;全局作用域中无法访问到局部作用域中的变量。
    3、全局变量第一种创建方式:在全局作用域下 var声明的变量是全局变量
    4、全局变量第二种创建方式:如果在函数内部,没有使用 var关键字声明直接赋值的变量也属于全局变量。(不建议使用)
局部作用域与全局作用域
  • 局部作用域
    1、在函数内部就是局部作用域,这个代码的名字只在函数的内部起作用
    2、调用函数时创建函数作用域,函数执行完毕之后,函数作用域销毁;
    3、每调用一次函数就会创建一个新的函数作用域,它们之间是相互独立的。
  • 全局作用域
    1、直接编写在 <script>..</script> 标签之中的JS代码;
    2、编写在一个单独的 JS 文件中的;
    3、全局作用域在页面打开时创建,页面关闭时销毁;
    4、在全局作用域中有一个全局对象 window(代表的是一个浏览器的窗口,由浏览器创建),可以直接使用。
函数作用域与块级作用域
  • 函数作用域
序号条件及情况 (具体示例可以参考: 链接
1调用函数时创建函数作用域,函数执行完毕以后,函数作用域销毁
2每调用一次,函数就会创建一个新的函数作用域,它们之间是互相独立的
3在函数作用域中,可以访问到全局作用域的变量
在全局作用域中,无法访问到函数作用域的变量
4当在函数作用域操作一个变量时,它会先在自身作用域中寻找,如果有就直接使用
5如果没有则向上一级作用域中寻找(作用域链),直到找到全局作用域
6如果全局作用域中依然没有找到,则会报错 ReferenceError
7在函数作用域也有声明提前的特性:
使用var关键字声明的变量,会在函数中所有的代码执行之前被声明
函数声明也会在函数中所有的代码执行之前执行
8在函数中,不使用var声明的变量都会成为全局变量
9定义形参就相当于在函数作用域中声明了变量
  • 块作用域
    1、什么是块作用域?参考小黄书中的例子:
// 循环体
for (var i = 0; i<10; i++) {
	console.log(i);
}
// try/catch 语句
try {
	undefined();		// 执行一个非法操作来制造一个异常
}
catch (err) {
	console.log(err);	// 能够正常执行
}
console.log(err);		// ReferenceError: err not found
     2、在ES6中引入的 let 关键字,用来在任意代码块中声明变量。比如:
if (..) {
	let a = 2;
}			// 这里会声明一个劫持了 if 的{..} 块的变量,并且将这个变量添加到这个块中。
     3、上面的情况属于 let 为其声明的变量隐式地劫持了所在的作用域块,同样还有显式的块(在声明中任意位置使用 { .. } 来为 let 绑定块):
if (true) {
	{		// 显式的块
		let a = 2;			
		console.log(a);	//2
	}
}
console.log(a);		// Uncaught ReferenceError: a is not defined

/*
	在这个例子中,在if内部显式地创建一个块,如需对其进行重构,
整个块都可以方便地移动而不会对 if 声明的位置和语义产生任何影响。
*/ 
     4、块作用域一般指变量和函数不仅可以属于所处的作用域,也可以属于某个代码块(通常指 { .. } 内部)
  • 函数不是唯一作用域单元,还有块作用域 { .. }
  • 有些人认为块作用域不应该完全作为函数作用域的替代方案。两种功能应该同时存在,开发者可以并且应该更具需要选择使用何种作用域,创造可读、可维护的优良代码。 – 《你不知道的JavaScript》

总结

理解编译的过程,有助于后面在学习函数、闭包、原型等复杂概念的时候,大致可以明白其中的运行过程。


我喜欢读别人写的文章,不仅仅是因为文章的价值。更重要的是读取不同人对于同一种技术的理解。在与多种思想交流以后,如果能够产生自己的思维火花,这样会让我受益良多。
然后我会试着写出自己的理解,一家之言,并非权威,只希望其中的一些个人见解能对读者有所帮助!当然也欢迎讨论!
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JavaScript 是一种解释性语言,这意味着它在执行代码时并不会先进行编译,而是直接解释执行代码。这也是为什么 JavaScript 可以直接在浏览器执行,而不需要经过任何预处理的原因。 在 JavaScript ,当你在网页包含了一段 JavaScript 代码,浏览器就会执行这段代码。当你在浏览器打开控制台,并输入一段 JavaScript 代码,浏览器也会立即执行这段代码。这就是 JavaScript 解释执行的过程。 在 JavaScript ,解释器会一行一行地读取代码,并立即执行。这也是为什么 JavaScript 代码必须按照正确的顺序书写的原因。如果你的代码有语法错误或逻辑错误,解释器就会在执行过程报告错误,并停止执行。 总的来说,JavaScript 解释执行的过程就是浏览器在读取和执行 JavaScript 代码时所进行的过程。 ### 回答2: JavaScript 是一种解释型语言,它的代码在运行时通过解释器进行解释执行,而不需要经过编译器的编译过程。JavaScript 的解释器会逐行解释代码,并在解释过程动态执行代码。 首先,JavaScript代码不需要显式地进行编译,在代码执行之前不会产生一个完整的编译结果。相反,JavaScript 解释器会逐行解释代码,并即时执行。这种即时执行的特性使得开发者能够更加迅速地测试和调试代码,提高了开发效率。 其次,JavaScript 解释器的工作方式与编译器不同。编译器将源代码编译为机器可执行的二进制代码,而解释器则是逐行解释代码,并将解释结果直接执行。解释过程,解释器会逐行扫描代码,将代码转换为机器指令并执行。这种即时翻译和执行的方式使得JavaScript代码能够适应不同平台和环境,无需重新编译。 总之,JavaScript 的解释执行并不依赖于编译编译代码,而是通过解释器进行逐行解释和执行。这种即时执行的方式使得JavaScript适用于快速开发和动态调试,并能够在不同平台和环境运行。 ### 回答3: javascript解释执行的确是编译编译代码。在传统的编译语言代码执行之前需要经过两个步骤:编译执行编译器会先将源代码转换成机器代码,再由计算机执行。 然而,javascript是一种解释型语言,不同于传统编译语言。在javascript代码并不是直接编译成机器代码,而是由javascript解释器逐行解释执行。 在javascript代码执行过程,解释器会将代码逐行解析并执行。它会先进行语法解析,将代码分解成语法树,并进行词法分析,确定每个标识符的含义和作用域。然后,解释器会对代码进行逐行解释和执行,一边解析一边执行。 这种解释执行的方式带来了一些优势。首先,代码无需编译成机器码,可以直接在平台上运行,简化了开发流程。其次,解释器能够根据不同平台和环境的特性进行实时的优化,提高程序的性能和效率。 然而,与编译型语言相比,javascript的解释执行也存在一些劣势。由于每次都需要解释执行代码,相同的代码可能会被多次解释执行,导致性能下降。为了解决这个问题,javascript引入了即时编译(Just-In-Time Compilation)技术,将一些频繁执行代码编译成机器码,减少解释执行的时间,提高性能。 综上所述,javascript是一种解释型语言,它的执行过程是由解释器逐行解释和执行编译编译代码。这种解释执行的方式简化了开发流程,但也存在性能方面的劣势,通过即时编译技术可以提高性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值