前言
在编程的世界里,底层逻辑如同灯塔,引领着开发者们在代码的海洋中航行。它们确保了程序的可读性、可维护性和正确性。本文将深入探讨几个核心概念:词法分析、作用域、以及变量声明的差异,以及var
与let
、const
的区别。
⚡ 词法分析:
词法分析。词法分析也被称为分词。例如,一个简单的加法表达式a + b
,在词法分析后会被识别为三个元素:标识符a
、运算符+
和标识符b
。这一步骤为后续的语法解析奠定了基础,确保每一部分都能被正确解析和处理。
⚡ 作用域:
在代码执行的舞台上,作用域扮演着至关重要的角色,它定义了变量的可见性和生命周期。作用域分为两种基本类型:全局作用域和函数作用域。
全局作用域中的变量在整个程序中都可被访问,而函数作用域内的变量仅在该函数内部有效。这一规则确保了变量使用的清晰隔离,减少了命名冲突的可能性。
var a = 1
function damn(){
var a = 2
}
damn()
console.log(a);
例如上面这个例子,大家认为输出的a的值应该是多少呢?很简单,a = 1,虽然函数中已经声明了a = 2并且已经调用了函数,但是输出方法在全局作用域中,浏览器的计算引擎会在全局环境中寻找a的值,故结果为a = 1.通过下图可以加强理解
⚡ 词法作用域:变量声明的地方。这意味着变量的可用性在其声明时就已经确定,无论函数如何调用或代码如何执行。
⚡ 欺骗词法作用域:eval()与with(){}
JavaScript中,eval()
函数和with
语句能够“欺骗”词法作用域。
eval()
能够执行一个字符串作为代码,使其所在的作用域能够访问到原本不可见的变量。让原本不属于这里的代码,变得好像天生就定义在这里了一样。
function damn(a,str){
eval(str); // var b = 2
console.log(a,b);
}
输出结果如下:
而with
语句通过临时修改作用域链,使得对未定义属性的访问可能意外地创建全局变量,当修改的对象中不存在的属性时,这个属性会被泄露到全局变成全局变量。
function foo(obj){
with(obj){
a = 2;
}
}
var o1 = {
b: 1,
}
foo(o1)
console.log(a); // 2
输出结果如下:
其实这里本来应该是报错,但是with(){}的使用使变量a泄露到全局当中了,所以能被输出。
所以在日常的开发过程中可以尽量避免使用eval()与with(){}
⚡ var, let与const:
var
声明的变量具有两个特点:
1.var 声明的变量会存在声明提升。
- var 在全局声明的变量会被添加到window对象上。
相比之下,ES6引入的let
和const
提供了更精细的控制。let
允许在块级作用域(如循环体或条件语句内)声明变量,解决了var
重复声明和变量提升的问题。
而const
声明的是常量,不可以修改值,任何尝试修改其值的操作都会引发错误,增强了代码的安全性和可读性。
⚡ 块级作用域
{} + let || const -> {let} {const}
块级作用域是指变量、常量或者某些类型的语句(比如if、for循环)的有效范围仅限于该代码块内部。在这一作用域内声明的变量,在块外是无法访问的。
由此我们引申出一个概念:暂时性死区
let a = 1
{
console.log(a); // 暂时性死区
let a = 2
}
可能大部分人都认为上述代码输出结果为a = 1,但是实际输出结果却为:Cannot access 'a' before initialization
,无法在a初始化之前访问它。
因为{let}形成了一个块级作用域,会先访问自身作用域内的a,而由于先输出再声明a = 2,导致自身作用域内的a无法被访问,然后在全局中又访问不到变量a,所以结果输出报错。
结语
今天有关JS中的作用域与底层逻辑的分享到这里就结束了,希望对你有所帮助。😉