🚀 JavaScript执行江湖全揭秘:从调用栈到作用域链的奇幻漂流 🌊

44 阅读4分钟

🚀 JavaScript执行江湖全揭秘:从调用栈到作用域链的奇幻漂流 🌊

🌍 第零章:代码执行前的神秘仪式——编译阶段

JavaScript虽被称为"解释型语言",但在执行前会经历闪电编译(通常<1ms)。这个阶段藏着变量提升的核心秘密:

// 你以为的执行顺序:
console.log(name); // 报错?
var name = "wql";

// 实际发生的编译魔法:
var name = undefined; // ← 变量提升!
console.log(name);    // 输出undefined
name = "wql";

🔍 编译阶段三巨头

  1. 变量登记处:扫描var声明,分配undefined
  2. 函数保险库:完整保存函数声明
  3. TDZ禁区:标记let/const声明(但不上锁)

📊 内存快照对比

阶段var变量函数let/const
编译阶段undefined完整函数🚫不可访问
执行阶段实际值可调用已初始化

🔍代码对比

console.log(name); // 报错 输出ReferenceError
let name = "wql";

console.log(name); // 输出undefined
var name = "wql";


console.log(fun()) // 输出 fun 函数的返回值 :undefined 或者返回值类型的默认值
    console.log(fun) // 输出 fun 函数本身 :[Function: fun],传递fu函数本身
function fu(){

    return "wql"
}

🏰 第一章:时间旅行者的烦恼——变量提升的时空悖论

1.1 var的"半吊子提升"
function timeParadox() {
  console.log(age); // undefined
  if (false) {
    var age = 18; // 这个声明依然会提升!
  }
}

💡 奇特现象:即使if条件为false,var声明仍会提升!这是因为JS的函数级作用域特性。

1.2 函数的"VIP提升通道"
hero(); // "亚索!"

function hero() {
  console.log("亚索!");
}

villain(); // TypeError: villain is not a function

var villain = function() {
  console.log("劫!");
}

⚡ 关键差异:函数表达式不会提升!只有函数声明享受VIP待遇。

1.3 let/const的"安检流程"
{
  console.log(weapon); // ReferenceError
  let weapon = "无尽之刃";
}

🛑 TDZ规则

  1. 进入块作用域时创建标识符
  2. 直到声明语句前都处于"暂时性死区"
  3. 任何访问尝试都会触发错误

🌐 第二章:作用域链的量子纠缠

2.1 作用域的三重宇宙
// 全局宇宙
const galaxy = "银河系";

function outer() {
  // 恒星系
  const star = "太阳";
  
  function inner() {
    // 行星世界
    const planet = "地球";
    console.log(galaxy + "-" + star + "-" + planet); // "银河系-太阳-地球"
  }
  
  return inner;
}

📡 作用域链查找流程

inner作用域 → outer作用域 → 全局作用域 → null
2.2 闭包的"时空胶囊"
function createCounter() {
  let count = 0;
  return {
    add: () => count++,
    get: () => count
  };
}
// 调用 createCounter 函数,创建一个计数器对象
const counter = createCounter();
// 调用计数器对象的 add 方法,将计数器的值加 1
counter.add();
// 调用计数器对象的 get 方法,获取当前计数器的值,并将其打印到控制台
console.log(counter.get()); // 1

🔗 闭包三要素

  1. 外层函数返回内层函数
  2. 内层函数访问外层变量
  3. 外层变量被长期保持

🏗️ 第三章:执行上下文的建筑工地

🔗 执行上下文

 js 代码要执行 <-- 执行机制(调用栈)<--  函数入栈(操作系统) <-- 执行上下文(代码和变量声明的关系)<-- 作用域 + 变量提升 + 可代码运行 <--  运行阶段

image.png

3.1 全局执行上下文栈的叠盘子艺术
var name ='wzy'
function fu(){
    
}
let a = 10

📜 调用栈轨迹

image.png

3.2 执行上下文栈的叠盘子艺术
var a = 1;
function fn(a){
    var a = 2;
    function a(){}
    var b = a;
    console.log(a);
}
fn(3); // 输出2
console.log(a); // 输出1

📜 调用栈轨迹

image.png


🛠️ 第四章:现代JS编程的倚天剑与屠龙刀

4.1 块级作用域的最佳实践
//setTimeout的回调函数会在 for循环结束后才开始执行(需要的重点)

// 旧时代 es5的特性
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 10); // 输出3次3
}

// 新时代 es6的新功能
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 10); // 输出0,1,2
}

🔮 终章:成为JavaScript执行大师的十二道试炼

  1. 练习1:画出以下代码的作用域链
const globalVar = 1;
function outer() {
  const outerVar = 2;
  function inner() {
    const innerVar = 3;
  }
}

2. 练习2:预测输出顺序

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);

3. 练习3:修复变量提升问题

for (var i = 0; i < 3; i++) {
  button[i].onclick = function() {
    console.log(i); // 总是输出3
  }
}

🎉 致未来的JS宗师
掌握调用栈就像获得时间宝石,理解作用域链如同解锁空间宝石。当你能在脑海中构建完整的执行上下文宇宙时,异步编程、性能优化、框架原理都将成为你的掌中之物。继续前行吧,JavaScript世界的无限可能正在等待你的探索!🚀不懂可以私信博主!或者留言!