编译原理
词法分析:
词法分析(),又称扫描(Scanning),是编译过程中的第一个阶段。此阶段的任务是将输入的源代码字符串转换为一系列标记(Token)或词法单元(Lexical Unit)。这些标记是源代码的基本构成元素,如关键字、标识符、运算符、分隔符和常量等。例如var a = 2就会把它分析出来为"var","a","=","2"这四部分。
语法分析:
它根据词法分析得到的词法单元(Token)序列,然后对这些词法单元流(Token Stream)进行解析,然后将这些词法单元转换成抽象语法树(Abstract Syntax Tree, AST)语法树是程序语法结构的直观表示,后续的代码生成和优化等阶段都会基于语法树进行。
生成代码:
编译器或解释器会根据前面阶段(如语法分析和语义分析)构建的语法树(如抽象语法树,AST)和符号表等信息,生成目标机器或虚拟机能够执行的代码(像字节码,机械码等)。例如上面var a = 2就会被转换为一个机械指令,创建一个变量a,将值2存于变量a当中。
作用域
js有全局作用域和函数作用域,作用域法则;是内层作用域可以访问外层作用域外层作用域不能访问内层作用域
词法作用域
词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,可以看下面的例子
function foo(a) {
var b = a * 2;
function bar(c) {
console.log( a, b, c );
}
bar( b * 3 );
}
foo( 2 ); //输出结果为 2, 4, 12
1,包含着整个全局作用域,其中只有一个标识符:foo。
2,包含着 foo 函数方法所创建的作用域,其中有三个标识符:a、bar 和 b。
3,包含着 bar 函数方法所创建的作用域,其中只有一个标识符:c。
欺骗词法作用域
如果词法作用域完全由写代码期间函数所声明的位置来定义,怎样才能在运行时来“修 改”(也可以说欺骗)词法作用域呢?JavaScript 中有两种机制来实现这个目的
1,eval
function foo(str,a){
eval(str)
console.log(a,b);
}
foo('var b = 3',1)
调用中的 "var b = 3" 这段代码会被当作本来就在那里一样来处理。由于那段代码声明了一个新的变量 b,因此它对已经存在的 foo(..) 的词法作用域进行了修改,就相当于下面的代码。
function foo(str,a){
var b=3
console.log(a,b);
}
foo('var b = 3',1)
事实上,和前面提到的原理一样,这段代码实际上在 foo(..) 内部创建了一个变量 b,并遮蔽
了外部(全局)作用域中的同名变量,
2,with
with一般用于批量修改一个对象中的属性值,例如下面代码
var obj = {
a: 1,
b: 2,
c: 3
}
with (obj) {
a = 2
b = 3
c = 4
}
console.log(obj)
用with修改后的输出结果为{ a: 2, b: 3, c: 4 }。
在这里我们要讲它如何作为欺骗语法,如果我们要修改的对象中不存在的属性值,那么该属性值就会泄露到全局作用域上面,例如下面代码
var obj = {
a: 1
}
with(obj){
b=2
}
console.log(obj,b);
这里with修改属性b,但在它修改的对象中不存在b这个属性,那么修改后的属性值就会泄露为全局变量,下面是代码的输出结果
块级作用域
for (var i = 0; i < 10; i++) {
//console.log(i);
}
console.log(i);
for循环是一个语句,在JavaScript中只有全局作用域和函数体作用域,显然var i = 0是全局中的标识符,所有循环体外面也能访问到i,这里的输出结果就是
但是我们把
var关键字换为let,就会形成块级作用域
for (let i = 0; i < 10; i++) {
//console.log(i);
}
console.log(i);
此时全局下面就访问不到i了
所以块级作用域是
let + {}形成的
那是整个{}里面的标识符都会变为块级作用域吗,看看下面代码
if (true) {
let a = 1
var b = 2
}
console.log(b);
在{}外面a肯定是访问不到了,那么b呢,下面是代码的输出结果
由此可见
b是可以正常拿到的,所以let+{}并不是将整个{}里面的内容都变为块级作用域,而是将let关键字声明的变量转变为块级作用域
结语
本文概述了编译原理中的词法分析、语法分析和代码生成阶段,以及JavaScript中的作用域机制,特别是词法作用域、块级作用域以及欺骗词法作用域的两种方式(eval和with)。
词法分析将源代码转换为标记序列,语法分析将这些标记转换为抽象语法树。代码生成阶段则基于语法树等信息生成可执行代码。
JavaScript具有全局作用域和函数作用域,词法作用域由代码书写位置决定。let和{}形成块级作用域,限制变量可见性。eval和with机制可“欺骗”词法作用域,但使用需谨慎,以避免潜在风险