前端面试 P009:执行上下文、调用栈与词法作用域
目前顯示中文(中國)原文
来源:方格前端讲面试 · 9.执行上下文和作用域链是什么?,12 分 58 秒。
回看定位:00:28 上下文与作用域链 · 06:40 词法作用域 · 10:36 闭包
三个概念不要混为一谈
- 执行上下文:规范描述代码执行时状态的抽象,包含词法环境等信息。
- 调用栈:同步调用的上下文按后进先出的方式进入、退出。
- 作用域链:访问标识符时,沿词法环境的外部关联逐级查找。
“谁调用了我”决定调用栈上的关系;“我在哪里创建”决定普通函数访问外部变量的词法关联。这两条关系不一定相同。
const value = 'global';
function read() {
return value;
}
function caller() {
const value = 'caller';
return read();
}
console.log(caller()); // global
read 在外层创建,不会因为从 caller 内部调用,就自动访问 caller 的局部 value。this 也不能直接按变量的词法查找规则推导,见 前端面试 P010:普通函数、箭头函数与 this。
同步调用栈
全局脚本调用 first,first 调用 second 时,可以用下面的过程理解:
进入 first:全局 → first
进入 second:全局 → first → second
second 返回:全局 → first
first 返回:全局
上下文出栈,不等于相关词法环境中的所有数据都必须立即销毁;闭包仍可能让部分绑定保持可达。
闭包保存的是访问绑定的能力
function makeCounter() {
let count = 0;
return () => ++count;
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
makeCounter 返回后,counter 仍能访问创建时关联的 count。它不是每次自动复制一份固定为 0 的数值。再次调用 makeCounter 则会产生另一组独立绑定。
对视频简化说法的补充
视频把上下文主要分成全局和函数,适合入门,但不是完整穷举:eval、模块等执行也有相应上下文;多个 realm 不能简化为整个应用永远只有一个全局环境。
“函数定义时作用域就确定”是指词法外部关联,不是所有变量值都被冻结。也不能据此推断引擎必须保留一个完整、永不回收的物理栈帧。
闭包产生额外存活数据并不自动构成内存泄漏。只有无用数据仍被长期可达的引用保留,才需要排查。引擎也可能优化变量访问,不能简单凭作用域层数断言实际性能。