JavaScript 立即执行函数(IIFE)运行时行为分析笔记
一、代码示例
(function() {// IIFE 函数作用域console.log(typeof foo); // 第一次打印console.log(typeof bar); // 第二次打印function foo() {} // 函数声明var bar = 10; // 变量声明console.log(typeof foo); // 第三次打印console.log(typeof bar); // 第四次打印
})();
二、作用域结构分析
IIFE 形成了嵌套的作用域结构,决定了变量的可见性:
- 全局作用域:包含 IIFE 函数本身(IIFE 作为全局作用域的一个函数)。
- IIFE 函数作用域:嵌套在全局作用域内,包含
foo
(函数)和bar
(变量)的声明,是本次分析的核心作用域。
三、变量提升(Hoisting)机制
JavaScript 引擎在编译阶段会将声明(函数声明、变量声明)提升到当前作用域顶部,赋值操作保留在原地,代码实际执行顺序如下:
(function() {// 编译阶段提升的内容:function foo() {} // 函数声明整体提升(含函数体)var bar; // 变量声明提升(初始值为 undefined)// 执行阶段:按顺序执行console.log(typeof foo); // 第一次打印console.log(typeof bar); // 第二次打印bar = 10; // 变量赋值(保留在原位置)console.log(typeof foo); // 第三次打印console.log(typeof bar); // 第四次打印
})();
- 函数声明提升:函数声明(
function foo() {}
)会被完整提升到作用域顶部,在声明前即可访问。 - var 声明提升:
var
声明的变量(var bar = 10
)仅提升声明部分,赋值操作留在原地,声明前访问时默认值为undefined
。
四、执行上下文生命周期
执行上下文是函数运行时的环境,其生命周期分为创建阶段(编译时)和执行阶段(运行时)。
1. 创建阶段(编译时)
此阶段为执行做准备,确定变量存储、作用域链和 this 指向:
- 变量对象(Variable Object,VO):存储当前作用域内的声明(函数/变量)
VO = {foo: function() {}, // 函数声明整体提升bar: undefined // var 变量声明提升,初始值为 undefined
}
- 作用域链:由内到外依次关联嵌套的作用域,此处为
[IIFE 函数作用域, 全局作用域]
- this 指向:非严格模式下指向全局对象(浏览器中为
window
)
2. 执行阶段(运行时)
按代码顺序执行,修改变量对象中的值:
- 执行赋值操作(
bar = 10
),变量对象更新为:
VO = {foo: function() {}, // 函数声明保持不变bar: 10 // 变量赋值后的值
}
- 按顺序执行代码,完成打印等操作。
五、四次打印结果详解
打印位置 | 变量 | 输出结果 | 原因解释 |
---|---|---|---|
第一次 | typeof foo | "function" | 函数声明被提升到作用域顶部,声明前可直接访问(函数类型) |
第二次 | typeof bar | "undefined" | bar 仅提升声明,未执行赋值,默认值为 undefined (undefined 类型) |
第三次 | typeof foo | "function" | 函数声明未被修改,仍为函数类型 |
第四次 | typeof bar | "number" | bar 已执行赋值(bar = 10 ),类型为数字(number 类型) |
六、编译阶段与运行阶段的核心差异
JavaScript 引擎对代码的处理分为两个明确阶段,是理解变量提升、作用域等机制的关键:
阶段 | 时间点 | 核心工作 | 影响范围 |
---|---|---|---|
编译阶段 | 代码执行前 | 1. 解析代码结构,确定作用域嵌套关系; 2. 处理声明提升(函数声明整体提升,var 声明仅提升名称) | 作用域结构、变量归属 |
运行阶段 | 代码执行时 | 1. 创建执行上下文(变量对象、作用域链、this 指向); 2. 逐行执行代码,修改变量值 | 变量实际值、this 指向、运行状态 |
七、关键知识点补充
- 函数声明与变量声明的优先级:
同作用域内若存在同名函数声明和变量声明,函数声明会覆盖变量声明(但变量赋值会覆盖函数声明)。
示例:
(function() {var foo = 'bar'; console.log(foo); // 输出 "bar"(变量赋值覆盖函数声明)function foo() {}
})();
- var 与 let/const 的差异:
let/const 声明的变量存在“暂时性死区(TDZ)”,声明前访问会报错(ReferenceError),而 var 声明无此限制。
示例:
(function() {console.log(bar); // 报错:ReferenceError(处于 TDZ)let bar = 10;
})();
- 作用域与执行上下文的关系:
作用域是静态结构(定义时确定),决定变量可见范围;执行上下文是动态状态(运行时创建),存储变量实际值,二者通过作用域链关联。
通过以上分析,可清晰理解 IIFE 运行时的变量提升、作用域交互及执行上下文变化,进而掌握 JavaScript 核心运行机制。