JavaScript 的执行分为两个阶段:编译阶段和执行阶段。
javascript 体验AI代码助手 代码解读复制代码// 执行上下文示例
function foo() {
console.log('foo');
bar();
}
function bar() {
console.log('bar');
}
foo();
// 调用栈过程:
// 1. 全局执行上下文入栈
// 2. foo() 执行上下文入栈
// 3. bar() 执行上下文入栈
// 4. bar() 执行完成出栈
// 5. foo() 执行完成出栈
// 6. 全局执行上下文出栈每个执行上下文包含:
var 声明的变量let、const 声明的变量变量提升是指 JavaScript 在编译阶段将变量和函数声明移动到其作用域顶部的行为。
【1.js】变量提升示例
javascript 体验AI代码助手 代码解读复制代码showName();
console.log(name);
var name = '路明非';
function showName(){
console.log('函数showName 执行了');
}
// 实际执行顺序(编译后):
// function showName(){ console.log('函数showName 执行了'); }
// var name; // 提升但未赋值
// showName(); // 输出:函数showName 执行了
// console.log(name); // 输出:undefined
// name = '路明非';运行结果:
javascript 体验AI代码助手 代码解读复制代码函数showName 执行了
undefinedJavaScript 最初是为了给页面添加简单的动态效果,设计周期极短。为了快速实现,采用变量提升是最简单的方式:
【3.js】变量覆盖示例
javascript 体验AI代码助手 代码解读复制代码var name = '路明非';
function showName(){
console.log(name);
if(false){
var name = 'sadas';
}
}
showName();
// 实际执行:
// var name;(全局)
// function showName(){
// var name;(函数内提升)
// console.log(name);
// if(false){ name = 'sadas'; }
// }
// name = '路明非';
// showName(); // 输出:undefined(函数内的 name 覆盖了全局的)运行结果:
javascript 体验AI代码助手 代码解读复制代码undefined原因分析: 函数内部的 var name 被提升到函数顶部,初始值为 undefined,覆盖了对外部 name 的访问。
javascript 体验AI代码助手 代码解读复制代码// 本应销毁的变量因为提升而没有及时销毁
function test() {
for (var i = 0; i < 3; i++) {
// 循环体
}
console.log(i); // 输出:3(i 应该在循环结束后销毁)
}
test();在任何地方都能访问,生命周期等于页面生命周期。
javascript 体验AI代码助手 代码解读复制代码var globalVar = "我是全局变量"; // 全局作用域
function myFunction() {
console.log(globalVar); // 可以访问
}
myFunction();
console.log(globalVar); // 可以访问只能在函数内部访问,生命周期等于函数执行周期。
【2.js】局部作用域示例
javascript 体验AI代码助手 代码解读复制代码var globalVar = "我是全局变量";
function myFunction() {
var localVar = "我是局部变量";
console.log(globalVar); // 输出:我是全局变量
console.log(localVar); // 输出:我是局部变量
}
myFunction();
console.log(globalVar); // 输出:我是全局变量
console.log(localVar); // 报错:localVar is not defined运行结果:
vbnet 体验AI代码助手 代码解读复制代码我是全局变量
我是局部变量
我是全局变量
ReferenceError: localVar is not definedES5 不支持块级作用域,ES6 通过 let 和 const 支持。
【5.js】块级作用域示例
javascript 体验AI代码助手 代码解读复制代码// 块级作用域支持的语法
if(1){} // if 块
while(1){} // while 块
for(let i=0;i<10;i){} // for 块(使用 let)
function foo(){} // 函数作用域(不是块级)声明方式 | 存放位置 | 特性 |
|---|---|---|
var | 变量环境 | 变量提升、函数作用域 |
let/const | 词法环境 | 暂时性死区、块级作用域 |
执行到块级作用域时,let/const 声明的变量会被放入词法环境的一个独立区域,形成栈结构。
【7.js】执行上下文分析
javascript 体验AI代码助手 代码解读复制代码function foo() {
var a = 1; // 变量环境
let b = 2; // 词法环境(函数级)
{
let b = 3; // 词法环境(块级,入栈)
var c = 4; // 变量环境(无块级概念)
let d = 5; // 词法环境(块级,入栈)
console.log(a); // 1
console.log(b); // 3
}
console.log(b); // 2(块级 b 已出栈)
console.log(c); // 4(var 不受块级限制)
// console.log(d); // 报错:d is not defined(块级变量已出栈)
}
foo();运行结果:
体验AI代码助手 代码解读复制代码1
3
2
4执行上下文结构:
css 体验AI代码助手 代码解读复制代码执行上下文
├── 变量环境
│ ├── a: 1
│ └── c: 4
└── 词法环境(栈结构)
├── 函数级: { b: 2 }
├── 块级①: { b: 3 } ← 执行时压栈
└── 块级②: { d: 5 } ← 执行时压栈【8.js】暂时性死区示例
javascript 体验AI代码助手 代码解读复制代码let name = '刘锦苗';
{
console.log(name); // 报错:Cannot access 'name' before initialization
let name = '大厂的苗子';
}原因: 块级作用域内,let 声明不会被提升,但在声明前访问会形成暂时性死区。
【4.js】var vs let 对比
javascript 体验AI代码助手 代码解读复制代码// var 版本(3.js)
var name = '路明非';
function showName(){
console.log(name); // undefined(var 提升)
if(false){
var name = 'sadas';
}
}
showName();
// let 版本(4.js)
let name = '路明非';
function showName(){
console.log(name); // 输出:路明非(查找外层)
if(false){
let name = 'sadas';
}
}
showName();运行结果(4.js):
体验AI代码助手 代码解读复制代码路明非特性 | var | let/const |
|---|---|---|
作用域 | 函数作用域 | 块级作用域 |
变量提升 | 是(初始 undefined) | 是但未初始化(TDZ) |
重复声明 | 允许 | 不允许 |
全局声明 | 挂载到 window | 不挂载到 window |
暂时性死区 | 无 | 有 |
【6.js】var 在循环中的表现
javascript 体验AI代码助手 代码解读复制代码function foo(){
console.log(i); // undefined(var 提升)
for(var i=0;i<10;i++){}
console.log(i); // 10(循环结束 i 仍存在)
}
foo();如果使用 let,则不会有此问题:
javascript 体验AI代码助手 代码解读复制代码function foo(){
// console.log(i); // 报错:TDZ
for(let i=0;i<10;i++){}
// console.log(i); // 报错:i is not defined
}当访问一个变量时,JavaScript 会按照以下顺序查找:
undefinedcss 体验AI代码助手 代码解读复制代码执行上下文
┌─────────────────────────────┐
│ 词法环境(栈结构) │
│ ┌─────────────────────┐ │
│ │ 块级作用域②: {d:5} │ ← 栈顶(当前执行)
│ ├─────────────────────┤ │
│ │ 块级作用域①: {b:3} │ │
│ ├─────────────────────┤ │
│ │ 函数级: {b:2} │ │
│ └─────────────────────┘ │
├─────────────────────────────┤
│ 变量环境 │
│ ┌─────────────────────┐ │
│ │ a: 1, c: 4 │ │
│ └─────────────────────┘ │
└─────────────────────────────┘let/const + 词法环境栈实现let/const 声明前不可访问的区域javascript 体验AI代码助手 代码解读复制代码// ✅ 推荐:使用 let/const 避免变量提升问题
let name = '张三';
if (true) {
let name = '李四';
console.log(name); // 李四
}
console.log(name); // 张三
// ❌ 不推荐:依赖变量提升
console.log(x); // undefined(容易造成困惑)
var x = 10;
// ✅ 推荐:变量先声明后使用
let y = 10;
console.log(y); // 10JavaScript 采用"一国两制"策略,在同一套执行上下文中用变量环境和词法环境分别处理 var 和 let/const,既保持了向下兼容,又实现了更合理的块级作用域。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。