JavaScript 执行上下文和作用域详解

news/2025/3/25 3:47:35/文章来源:https://www.cnblogs.com/dawnkylin/p/18717967

如有错误和建议请评论告知。

执行上下文(Execution Context)

执行上下文是指 JavaScript 代码执行所需要的环境信息,包括当前代码的变量、函数声明、作用域链和 this 绑定等。JavaScript 引擎通过执行上下文栈(调用栈)管理代码的执行流程。

执行上下文的类型

  1. 全局执行上下文:程序首次运行时创建,全局唯一,生命周期与页面一致。
  2. 函数执行上下文:每次函数调用时创建(包括嵌套调用),函数执行完毕后销毁。
  3. Eval 执行上下文(不推荐使用):eval() 函数内部的代码有自己的上下文。

执行上下文的生命周期

  1. 创建阶段:
    • 创建变量对象(Variable Object, VO)(函数上下文中称为活动对象 AO),存储变量、函数声明和形参。
    • 建立作用域链(Scope Chain),由当前 VO 和所有父级 VO 组成。
    • 确定 this 的指向。
function foo(a) {var b = 2;function c() {}
}
// 创建阶段的活动对象(AO):
AO = {arguments: { 0: a, length: 1 },a: undefined,b: undefined,c: reference to function c()
};
  1. 执行阶段:
    • 变量赋值、执行代码。
    • 动态修改作用域链(如 with 或 catch 语句)。
  2. 回收阶段:
    • 执行完毕后,上下文从栈中弹出,等待垃圾回收。

变量提升(Hoisting)的本质

函数声明整体提升,变量声明仅声明提升(赋值留在原地)。

console.log(a); // undefined(变量提升)
var a = 10;bar(); // "Hello"(函数声明提升)
function bar() { console.log("Hello"); }

作用域(Scope)与作用域链

作用域是变量和函数的可访问性规则,JavaScript 采用词法作用域(静态作用域),由代码书写时的位置决定。

作用域的类型

  1. 全局作用域:最外层,通过 var 或未声明的变量隐式绑定到 window。
  2. 函数作用域:函数内部声明的变量,仅在函数内可用。
  3. 块级作用域(ES6+):通过 let/const 在 {} 内限定作用域。
if (true) {let blockScoped = 42;var functionScoped = 24;
}
console.log(functionScoped); // 24
console.log(blockScoped); // ReferenceError

作用域链的构建

每个执行上下文都有一个关联的作用域链,用于变量向上查找。

作用域链构建时机

作用域链的构建发生在函数定义时(而非调用时),这是词法作用域(静态作用域)的核心特征。函数会记录其定义时所处的作用域链,后续调用时直接复用。

作用域链组成

每个执行上下文的作用域链由一系列环境记录(Environment Records) 组成,具体包括:

  1. 当前环境记录:当前函数/块的变量、函数声明、形参等。
  2. 外部环境记录的引用([[OuterEnv]]):指向父级作用域的环境记录。
  3. 全局环境记录:最顶层的作用域。

作用域链构建的详细过程

  1. 函数定义阶段:JavaScript 引擎会为其创建闭包对象(即 [[Scopes]] 属性,可通过 debugger 或者 console.dir 查看),保存当前作用域链,也就是父作用域链。因此也可引出闭包产生的本质是基于作用域链的:函数通过作用域链保留其词法作用域的引用。
  2. 函数执行阶段:当函数执行(或被调用)时,引擎基于 [[Scopes]] 构建完整作用域链,并加入当前活动对象(AO)到作用域链的最前面,形成完整链inner.AO → outer.AO → global.VO,如果期间遇到 with 或者 catch 语句,那么再将对应对象加入作用域链前端,如果在 with 和 catch 中访问外部变量,那么加长的作用域链就会带来额外的性能消耗。解决方法:对于 with,尽量避免使用;对于 catch,可以将错误对象传入外部函数单独处理,这样 catch 块内就没有变量的访问了。
const obj = { message: "with" };
function outer() {const message = "outer";function inner() {console.log(message);with (obj) {console.log(message); // 通过作用域链访问外层 message}try {throw new Error("catch");} catch (e) {console.log(e.message);}}return inner;
}
const closure = outer();
closure();
// outer
// with
// catch

ES6 的块级作用域与暂时性死区(TDZ)

let/const 声明的变量在声明前不可访问(TDZ)。

console.log(a); // ReferenceError(TDZ)
let a = 5;

执行上下文与作用域的关系

  1. 执行上下文是动态的、即时的:每次函数调用都会创建新的上下文,函数执行完就销毁。
  2. 作用域链是静态的:由代码结构决定,与函数调用无关,所以一直存在。

常见误区与优化

  1. 循环中的闭包陷阱:
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100); // 输出 3 3 3
}
// 解决:使用 let 或 IIFE 创建新作用域
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100); // 0 1 2
}
  1. 避免污染全局作用域:
// 错误做法
function init() {data = "secret"; // 隐式全局变量!
}
// 正确做法
function init() {const data = "secret";
}
  1. 作用域链查找性能:
    • 访问局部变量最快,全局变量最慢(需遍历整个链)。
    • 缓存全局变量:const doc = document;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/884676.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【后端】简化部署设计方案V2——技术实现方案

一、安装部署核心内容包的结构 部署核心内容包包含了对操作系统的优化、中间件的安装配置和子平台的安装配置等类型。 1.1 现有交互式部署工具V1的小组件部署结构 目前的交互式部署工具V1进行细化的部署工作,V1工具为每个组件定义了完整的部署逻辑,同时V1工具也为每一个可变参…

最大连续和(单调队列dp)

这道题对取最大值的地方有要求,要先取最大值再入队

Mysql之B树

B-树 B-树(B树或B_树),这里的 B 表示 balance( 平衡的意思),B-树是一种多路自平衡的搜索树(B树是一颗多路平衡查找树) 它类似普通的平衡二叉树,不同的一点是B-树允许每个节点有更多的子节点。下图是 B-树的简化图。B-树有如下特点所有键值分布在整颗树中(索引值和具体dat…

Svelte 最新中文文档翻译(10)—— use: 与 Actions

前言 Svelte,一个非常“有趣”、用起来“很爽”的前端框架。从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1:Svelte 以其独特的编译时优化机制著称,具有轻量级、高性能、易上手等特性,非常适合构…

如何在啥也不懂的情况下将你的公众号接入DeepSeek或其它大模型

本文详细介绍了零基础用户如何借助AI工具将微信公众号接入DeepSeek等大模型实现智能回复的全流程。首先通过AI问答确定开源项目chatgpt-on-wechat,利用豆包AI分析项目结构后选择Docker部署方案。重点讲解了在Ubuntu系统配置国内镜像安装Docker、编写含中文注释的docker-compos…

rust学习十八.1、RUST的OOP和简单示例

很可惜,出于一些理由,rust抛弃了OOP的核心特性之一:继承 其中一个理由应该是至关重要的,但是解释的比较模糊:继承增加了复杂性的确,继承会让rust编译器变得更加复杂。rust编译器虽然足够体贴,但是它偏慢的编译速度也是很多人所吐槽的。 在我对rust编译了解更多之前,我对…

【后端】简化部署设计方案——内层设计方案

根据《简化部署设计方案V2》的内容,内层部署需要提供的 .sh 脚本应覆盖部署流程的各个阶段,确保组件能够顺利安装、配置、启动、检查状态,并支持升级和卸载。 一、 部署某组件所需的内层结构 左侧是约定的内层结构,右侧的其他部署的内容物,按需存放即可。1.1 scripts目录:…

H3C CX8028 GPFS并行文件系统全闪存储配置案例

H3C华三CX8028使用IBM的GPFS商业授权的一款全闪存储设备。本案中,客户机服务器与存储服务器均搭配双口200G网卡 ,配合200G的ROCE网络搭建集群。 GPRS文件系统中,存储集群不光需要把存储服务器组织在一起,客户机也需要在集群中声明身份,参与到集群之中,这并不像传统的集中…

【kali】在Kali Linux中安装Navicat17

Navicat 是笔者最喜欢的一款专业的数据库管理工具,不仅支持多种数据库类型,而且它提供了直观的用户界面和丰富的功能,帮助用户轻松管理和操作数据库,提高工作效率。 通过,Navicat我们是在Windows环境中部署的。本文以KALI为例,讲解如何在Linux环境中部署。 首先,我们从官…

URL 生成网站截图 API 数据接口

URL 生成网站截图 API 数据接口 网站工具 / 截图 高效生成网页截图 生成网页截图 / 图片输出。1. 产品功能支持全页截图和视窗截图; 支持自定义截图尺寸; 兼容移动设备截图; 支持暗黑模式截图; 固定参数请求,可以得到最新的站点截图; 快速高效的截图生成; 全接口支持 HT…

OCR识别表格中的参数及参数值

一、需求 识别固定表格中的文字,输出表格中的参数字典 二、整体思路找到一张含有表格的图片,利用mac自带的预览工具分析出图片每个单元格的对角线坐标, 使用程序根据图片文件和对角线坐标数组,分割图片为很多个小图片, 采用tesseract库识别单个单元格中的文字,并将识别出…

H800 Nvlink 基础环境配置(Ubuntu20.04 )

H100/800 SXM的8卡服务器在配置多机互联环境时,除了要安装Nvidia的GPU驱动,还需要安装FabricManager。 Nvidia 提供了在线安装和离线安装两种方式。Z项目H800 NVL 基础环境配置(Ubuntu20.04 ) 操作系统基础安装 配置源 本操作在Ubuntu 20.04下完成, 所有命令都以root身份执行…