1.前言
JavaScript 代码是如何运行的?
想象一下,JavaScript 引擎就像一个翻译官,它需要把我们写的代码"翻译"成计算机能懂的语言。以 Chrome 使用的 V8 引擎为例,这个过程分为两大步骤:
代码解析阶段
-
1.拆解代码(词法分析):
-
将代码拆分为最小语法单元
var a = 2; //拆分成 词法单元 标识符 等号 数值 分号; var a = 2 ;
-
-
2.检查语法(语法分析):
-
验证这些"单词"组合是否符合语法规则
-
验证语法正确性,并生成象语法树(AST),可以理解为代码的结构图
-
代码执行阶段
-
根据解析结果生成计算机能执行的指令
-
按照作用域规则查找和操作变量
2.作用域:变量的"活动范围"
-
定义:是在程序中定义变量的可访问性和生命周期范围。
-
作用域就像变量的"居住证",决定了:
- 这个变量在哪里能被访问(可访问性)
- 这个变量能"活"多久(生命周期)
-
-
作用域查找规则:
- 查找方向:**由内向外
-
a. 首先在 当前作用域中查找;
-
b. 若未找到,则向上查找父级作用域;
-
c. 继续向上查找,直至全局作用域;
-
d. 若全局作用域仍未找到,则抛出 ReferenceError;
-
- 查找方向:**由内向外
全局作用域(Global Scope):全球通行的"护照"
-
在所有函数外部声明的变量/函数拥有全局作用域
-
特点 :在代码任何地方都能访问,浏览器环境中挂载在 window 对象上
var globalVar = '我是全局变量'; // 在任何地方都能用 function showGlobal() { console.log(globalVar); // 这里能访问 } showGlobal(); console.log(globalVar); // 这里也能访问 -
风险:容易造成命名冲突(就像太多人叫"张三")
函数作用域(Function Scope): 函数内部的"门禁卡"
-
在函数内部声明的变量,仅在函数内部可访问
-
特点 :函数参数也属于函数作用域
function foo() { const localVar = '只有在这里能知道这个秘密'; console.log(localVar); // 正常访问 } foo(); console.log(localVar); //错误:变量未定义 -
好处:保护私有变量,避免外部干扰
块级作用域(Block Scope):块级{}的"临时通行证"
-
由 let 或 const 声明的变量,在 {} 块内有效
-
特点 : var 声明的变量没有块级作用域
if (true) { let blockVar = '我是块级变量'; const fixedVar = '常量块级变量'; console.log(blockVar); // 正常访问 } console.log(blockVar); // 错误:变量未定义 -
对比:
var会无视块级作用域(老式设计)
3. var、let和const 三兄弟对比
-
var 老大哥(有历史包袱)
-
var存在变量提升,声明会被提升到作用域顶部
consle.log(a); // 输出undefined var a =1-
在编译阶段:
var a // 声明提升,设为undefined consle.log(a); //输出undefined a = 1 // 然后赋值为1
-
-
var允许重复声明同一变量,后声明的会覆盖前面的
// var示例 function varTest() { var x = 1 if (true) { var x = 10; console.log(x); // 10 } console.log(x); // 10(函数作用域,仍可访问) } varTest() -
无视块级作用域:不够安全
if(true) { let d = 1 var c = 2 } console.log(d); // ReferenceError: d is not defined console.log(c); // 2
-
-
let 二弟(更严谨)
-
声明时可以不初始化,默认值为 undefined;
-
不允许在同一作用域内重复声明同一变量,会抛出 SyntaxError;
// let示例 function letTest() { if (true) { let y = 20; let y = 30; // SyntaxError: Identifier 'y' has already been declared console.log(y); // 20 } console.log(y); // 错误:y没定义 -
必须先声明后使用(暂时性死区)
let f = 1 if(true) { console.log(f); // 暂时性死区 let f = 2 }
-
-
const 三弟(最严格)
-
声明时必须初始化,且初始化后不可重新赋值 (但对象属性可修改)
-
不允许在同一作用域内重复声明同一变量,会抛出 SyntaxError;
// const示例 function constTest() { const z = 30; z = 40; // TypeError: Assignment to constant variable const obj = { a: 1 }; obj.a = 2; // 允许修改对象属性 } -
其他特性和 let 相同
-
4.实际应用建议
-
- 优先使用const :默认使用 const ,仅在需要重新赋值时使用 let
-
- 最小权限原则 :变量声明在最小必要作用域内
-
- 避免全局污染 :减少全局变量,使用模块化方案
-
- 警惕作用域链陷阱 :避免变量名冲突和意外遮蔽
-
- 理解暂时性死区 : let/const 声明前访问会抛出 ReferenceError