JavaScript作用域探秘:从词法分析到块级作用域的实践

430 阅读4分钟

JavaScript作用域探秘:从词法分析到块级作用域的实践

image.png

引言:理解编译过程的基石

在JavaScript的世界里,代码执行前的几个关键步骤——词法分析、解析、生成代码,共同构成了理解程序行为的基石。这一过程始于将源代码分解成可处理的单元,最终形成机器可执行的形式。

1. 词法分析(Lexical Analysis) :这是编译的第一步,负责将源代码分割成一系列称为“词法单元”或“令牌”的元素。在JavaScript中,有效的辨识符包括关键字(如iffunction)、标识符(变量名、函数名)、运算符、字面量(字符串、数字)、以及标点符号等。词法分析帮助解析器理解代码的基本结构。

2. 解析(Parsing) :接下来,解析器利用词法单元构建抽象语法树(AST)。这棵树描绘了代码的逻辑结构,让计算机能理解代码的意图。例如,它会识别出函数定义、条件语句、循环等结构。

3. 生成代码(Code Generation) :最后,基于AST,编译器或解释器生成可执行的机器代码或字节码,准备执行。

作用域:控制变量可见性的机制

全局作用域:在JavaScript中,全局作用域是最外层的作用域,任何在此作用域内声明的变量在整个脚本中都可见。

image.png 函数作用域:函数内部声明的变量具有局部作用域,仅在该函数内部可见。

var a=1

function foo(){
    var a=2
}
foo();

console.log(a);

这一段的a输出结果是多少? 1还是2?

image.png 结果为1, 跟你的想法一样吗?为啥是1嘞?

a在两个地方声明了,一个是在全局声明,一个是在foo函数中声明,在函数内部声明的变量仅在该函数内可见,第八行打印a是在全局中执行的,所以打印在全局声明的a的值。

那将console.log(a)换个位置嘞,结果会变吗?

var a=1

function foo(){
    var a=2
    console.log(a);
}
foo();

image.pngconsole.log(a)移至foo函数声明内部,结果就变为2了。

原因:找变量的顺序为,先在内部寻找,内部找不到则到外面去找,在foo中执行console.log(a),先在foo中找,是否存在a变量,存在,则打印foo中的a变量的值。所以这一段代码的输出结果为2。

词法作用域:JavaScript采用词法作用域,变量的作用域在编写时就已经确定,而非运行时。这意味着函数内部可以访问包含它的外部作用域的变量,但外部作用域无法访问函数内部的变量。

欺骗词法作用域

  • eval() :此函数能够执行一个字符串作为JavaScript代码。使用它时,字符串内的代码仿佛直接在当前作用域定义,可能导致意外地访问或修改作用域内的变量。
  • with() :虽然不常用且已被许多开发者避免使用,with语句临时修改了当前作用域,允许直接访问指定对象的属性而无需前缀,这可能无意间将对象的属性设置为全局变量。

varlet:变量声明的进化

var的特性

  • 声明提升var声明的变量会自动被提升至其所在作用域的顶部,导致在声明前访问变量不会报错,但值为undefined

image.png

  • 全局污染:在全局作用域中使用var声明的变量会成为window对象的属性(在浏览器环境中)。

let的引入

  • 块级作用域letconst引入了真正的块级作用域,这意味着它们声明的变量只在最接近的花括号 {} 内有效,比如在循环体或条件语句内。
  • 无提升:与var不同,let声明的变量在声明前访问会引发ReferenceError

image.png

  • const的不变性const用于声明常量,一旦赋值后就不能改变。这有助于编写更安全、可维护的代码。

image.png

块级作用域的实践意义

引入letconst不仅解决了var带来的变量提升和全局污染问题,还使得编码实践更加清晰和安全。通过限制变量的作用域,开发者能更好地管理状态,减少因作用域混淆引发的错误。特别是在循环、条件语句中,let确保每次迭代或条件分支都有独立的作用域,避免了闭包陷阱和循环变量逸出的问题。 如: image.png 如果将let换成var 结果就发生了翻天覆地的变化

image.png

结语

JavaScript作用域及其相关概念是理解语言行为的关键。通过深入探索词法分析、解析、代码生成的基础,再到作用域的多层次理解,以及varlet的对比,我们能更有效地利用这些机制来编写健壮、易于维护的代码。随着ECMAScript标准的不断演进,掌握这些核心概念对于每一位JavaScript开发者而言至关重要,它们是构建复杂应用程序的基石。