第 1 卷
JavaScript 核心知识点全面解答
- JavaScript代码是如何被执行的?
JavaScript代码的执行并非逐行直接执行,而是分两个核心阶段完成,且依赖JavaScript引擎与执行环境(浏览器/Node.js)的协作:
- 编译阶段(也叫创建阶段/预处理阶段):JavaScript引擎先对代码进行扫描和解析,完成执行上下
文创建、变量提升、函数提升、作用域确定等工作,为执行阶段做准备,该阶段不执行具体的代码逻辑。
- 执行阶段:在编译阶段的基础上,逐行执行代码,进行变量赋值、函数调用、表达式计算等操作,
同时会创建变量对象(VO/AO)、维护作用域链、处理this绑定等。
简单来说,JS代码执行遵循“先编译,后执行”的原则,先做好“准备工作”,再逐行执行具体逻辑。
- 什么是JavaScript引擎?常见的JavaScript引擎有哪些?
(1)JavaScript引擎定义
JavaScript引擎是专门用于解析和执行JavaScript代码的虚拟机(可理解为“JS代码的运行容器”),它负责将人类编写的JavaScript源代码转换为计算机能识别的机器码,从而实现代码的运
行,是浏览器或Node.js等环境的核心组成部分之一。
(2)常见JavaScript引擎
- V8引擎:由Google开发的开源高性能引擎,采用C++编写,不仅用于Chrome浏览器,也是
Node.js、Electron等环境的核心引擎。
- SpiderMonkey:历史最悠久的JS引擎,由Mozilla开发,用于Firefox浏览器。
- JavaScriptCore(JSC):由Apple开发,用于Safari浏览器、iOS系统及MacOS的部分应用。
- Chakra:由微软开发,早期用于Edge浏览器(后期Edge已切换为V8引擎),也用于Windows系
统的部分内置应用。
- V8引擎是如何执行JavaScript代码的?
V8引擎执行JS代码采用**“解释执行+编译执行”结合的混合模式**,核心分为以下几个步骤,兼顾启动速度和运行性能:
- 解析(Parse):首先将JavaScript源代码通过解析器(Parser)转换为抽象语法树(AST,
- 解释执行(Ignition 解释器):V8内置的解释器Ignition会将AST转换为字节码(Bytecode),并
直接解释执行字节码。这一步的优势是启动速度快,无需等待编译完成即可执行代码,适用于代码首次执行或短期执行的场景。
- 编译执行(TurboFan 编译器):在解释执行的过程中,V8的监控模块会统计代码的执行情况(如
是否为热点代码⸺频繁执行的代码)。当检测到某段代码是热点代码时,会将该段代码的字节码和相关执行信息传递给编译器TurboFan,TurboFan会将字节码编译为高度优化的机器码
(Machine Code)。
- 优化与反优化:机器码的执行效率远高于字节码,后续再执行该段热点代码时,会直接执行机器
码,提升运行性能。若后续代码的执行环境发生变化(如变量类型改变,破坏了TurboFan的优化假设),V8会进行反优化操作,将机器码还原为字节码,重新由Ignition解释执行。
- V8引擎的架构包括哪些部分,它们的作用是什么?
V8引擎的核心架构(主要组件)及各自作用如下:
- 解析器(Parser / Full Parser)
◦ 作用:负责将JavaScript源代码转换为抽象语法树(AST),同时进行语法校验。另外,V8还
包含一个预解析器(Preparser),用于对未立即执行的代码(如函数内部代码)进行浅解析,只校验语法合法性,不生成完整AST,提升代码启动速度。
- 解释器(Ignition)
◦ 作用:① 将AST转换为字节码;② 直接解释执行字节码,保证代码的快速启动;③ 收集代码执
行的统计信息(如执行次数、变量类型等),为TurboFan编译器提供优化依据。
- 编译器(TurboFan)
◦ 作用:接收Ignition传递的热点代码字节码和统计信息,将字节码编译为高度优化的机器码,提
升热点代码的执行效率;同时支持反优化操作,在优化假设失效时还原字节码。
- 垃圾回收器(Garbage Collector,GC)
◦ 作用:自动管理内存,负责识别和回收JavaScript代码中不再使用的内存空间(无用对象),
避免内存泄漏。V8的GC采用分代回收策略(新生代+老生代),兼顾回收效率和内存利用率。
- 内置函数库(Built-ins)
◦ 作用:提供JavaScript的内置对象(如Object、Array、String)和内置方法(如
Array.prototype.push、 JSON.parse)的实现,这些功能大多由C++编写,提供高效的底层支持。
- 执行上下文管理器
◦ 作用:管理执行上下文的创建、入栈、出栈,维护执行上下文栈(调用栈),负责变量提升、
5. 解释什么是变量提升?为什么JavaScript语言中存在变量提升这一机
制?
(1)变量提升(Hoisting)定义
变量提升是JavaScript在编译阶段的一种预处理行为:引擎在扫描代码时,会将所有使用 var 声明的变量和函数声明(Function Declaration)提升到其所在作用域的顶部,但变量的赋值操作不会被提
升,函数表达式也不会被完整提升。
示例:
Code block
1console.log(a); // undefined(变量a被提升,未赋值)
2var a = 10;
3 4fn(); // "执行函数"(函数声明被完整提升)
5function fn() { 6console.log("执行函数");
7} 8
| 9 | console.log(fn2);// undefined(函数表达式仅变量提升,函数体不提升) |
|---|---|
| 10 | var fn2 = function() { |
| 11 | console.log("函数表达式"); |
| 12 | }; |
(2)变量提升存在的原因
- 方便代码编写(历史设计):早期JavaScript设计的初衷是简化开发,允许开发者在变量或函数声
明之前使用它们,无需严格遵循“先声明后使用”的规则,提升开发效率。
- 支持函数相互调用:若不存在变量提升,函数之间无法相互调用(如函数A调用函数B,函数B又调
用函数A),变量提升使得函数声明可以被提前处理,支持这种相互依赖的场景。
- 与JS引擎执行机制匹配:JS引擎采用“先编译后执行”的模式,编译阶段需要确定变量和函数的存
在性,变量提升是该执行机制的自然产物,为执行阶段提供了变量和函数的访问依据。
- 变量提升机制有哪些潜在的缺点?它可能导致哪些具体问题?
(1)潜在缺点
- 破坏代码可读性和可维护性:变量可以在声明前使用,导致代码的执行逻辑与书写顺序不一致,开
发者阅读代码时难以快速追踪变量的声明和赋值位置。
全站分卷索引 · 19 卷
- 1第 1 卷
- JavaScript 核心知识点全面解答
- (1)JavaScript引擎定义
- (2)常见JavaScript引擎
- ◦ 作用:负责将JavaScript源代码转换为抽象语法树(AST),同时进行语法校验。另外,V8还
- ◦ 作用:① 将AST转换为字节码;② 直接解释执行字节码,保证代码的快速启动;③ 收集代码执
- ◦ 作用:接收Ignition传递的热点代码字节码和统计信息,将字节码编译为高度优化的机器码,提
- ◦ 作用:自动管理内存,负责识别和回收JavaScript代码中不再使用的内存空间(无用对象),
- ◦ 作用:提供JavaScript的内置对象(如Object、Array、String)和内置方法(如
- ◦ 作用:管理执行上下文的创建、入栈、出栈,维护执行上下文栈(调用栈),负责变量提升、
- 5. 解释什么是变量提升?为什么JavaScript语言中存在变量提升这一机
- (1)变量提升(Hoisting)定义
- (2)变量提升存在的原因
- 2第 2 卷
- 3第 3 卷
- 4第 4 卷
- (3)执行上下文的生命周期
- ◦ 创建变量对象(Variable Object,VO)/ 活动对象(Activation Object,AO):初始化变量、
- (4)执行上下文对JavaScript代码执行的影响
- (1)全局代码执行过程
- ◦ 创建全局变量对象(VO),初始化全局变量( var 声明的变量设为 undefined)、全局函
- ◦ 若遇到 let / const 声明的变量(未被提升到全局顶部),则在其声明位置完成初始化(未
- ◦ 全局执行上下文仍保留在调用栈中(直到页面关闭/进程退出),等待事件触发(如定时器、
- ◦ 绑定函数 this 指向(根据调用方式确定,如普通调用指向全局,对象方法调用指向对象
- ◦ 创建活动对象(AO),初始化函数参数(形参赋值为实参,未传递的形参设为
- (3)核心总结
- 12. JavaScript在执行的过程中会产生AO、GO、VO、LE、VE、ER都是什
- 5第 5 卷
- 核心关联
- (1)原型(Prototype)概念
- ◦ 每一个JavaScript对象(除 null 和 undefined 外)都有一个内置的隐藏属性
- ◦ 这个属性是隐藏的,无法直接访问(早期浏览器提供 __proto__ 属性作为访问接口,ES6后
- ◦ 对象可以继承其原型对象上的所有属性和方法(当访问对象的某个属性/方法时,若对象自身没
- ◦ 每一个JavaScript函数(除箭头函数外)都有一个公开的 prototype 属性,该属性指向一个
- ◦ 当使用 new 关键字调用构造函数创建实例对象时,实例对象的 [[Prototype]](隐式原
- ◦ 原型对象上通常会定义所有实例共享的属性和方法,避免每个实例都创建重复的方法,节省内
- 6第 6 卷
- ◦ 若没有原型机制,每个对象实例都需要单独定义其方法和共享属性,这会导致大量重复代码,
- ◦ 原型机制允许将所有实例共享的方法和属性定义在构造函数的 prototype(原型对象)上,
- ◦ 示例:所有 Person 实例共享 sayHello 方法,无需为每个实例创建独立的 sayHello 函
- ◦ JavaScript没有类(ES6的 class 是语法糖,底层仍基于原型),原型链是实现对象继承的唯
- ◦ 通过将一个对象的原型指向另一个对象,即可让前者继承后者的所有属性和方法;通过原型链
- ◦ 示例: Person.prototype 的原型指向 Object.prototype,因此 Person 实例继承
- ◦ 原型链是动态的:若在原型对象上新增属性或方法,所有继承该原型的实例对象(无论创建于
- ◦ 这种动态扩展能力让JavaScript对象更加灵活,可随时调整对象的行为,适应复杂的开发场
- ◦ 原型机制允许基于已有对象快速创建新对象(如 Object.create(proto)),无需定义复
- 15. 原型链的终点是什么,以及如何在代码中打印出一个对象的原型链直
- (1)原型链的终点
- (2)代码打印对象的原型链直至终点
- 7第 7 卷
- 8第 8 卷
- • 适用场景:函数的普通调用(独立调用,无任何上下文对象),如全局环境中调用函数、函数作为
- • 绑定规则: this 指向全局对象(浏览器: window;Node.js: global(模块顶层为
- • 特殊情况:严格模式( use strict)下,默认绑定的 this 为 undefined,而非全局对
- • 示例:
- (2)隐式绑定(Implicit Binding)
- • 适用场景:函数作为对象的方法被调用(函数挂载在某个对象上,通过对象.函数() 的方式调
- • 示例:
- (3)显式绑定(Explicit Binding)
- • 核心方法:
- • 绑定规则: this 指向手动指定的 thisArg(若 thisArg 为 null / undefined,则触发
- • 示例:
- • 绑定规则: this 指向新创建的实例对象(构造函数执行时, this 绑定到即将返回的实例
- 9第 9 卷
- 10第 10 卷
- • 输出结果:
- 面试题7:绑定优先级(new > bind > 隐式)
- • 输出结果: 20( new 绑定优先级高于 bind, this 指向新实例 p, p.a=20,而非
- 19. 'var'、'let'和'const'用于变量声明,它们之间有何区别?请从作用
- 域、提升(hoisting)、以及是否允许重新赋值和重新声明的角度,详细比
- (1)核心维度对比表
- (2)逐维度详细说明
- • var:仅支持函数作用域和全局作用域,无块级作用域。这意味着 var 声明的变量会穿透 if、
- • let/const:支持块级作用域、函数作用域和全局作用域。变量仅在其声明的 {} 块内可访问,块外
- • var:存在完整的变量提升。 var 声明的变量会被提升到其作用域的顶部,且初始值被设置为
- 11第 11 卷
- • let:允许重新赋值。与 var 一致,声明后的变量可以多次修改值,唯一区别是作用域和提升特
- • const:不允许重新赋值。 const 声明的是“常量”,一旦赋值,变量指向的内存地址不可修改
- • var:允许在同一作用域内重复声明同一个变量,后续声明会覆盖前面的声明(仅变量声明覆盖,
- • var:在全局作用域声明的变量会挂载到全局对象(浏览器 window、Node.js global)上,可
- • let/const:在全局作用域声明的变量不会挂载到全局对象上,仅在全局作用域可见,不污染全局对
- (3)使用场景建议
- 20. 解释JavaScript中的Proxy对象是什么以及它是如何工作的。它通常用
- (2)Proxy的工作原理
- ◦ target:目标对象(可以是任意类型的对象,如普通对象、数组、函数等),是代理的最终
- ◦ handler:拦截器对象,包含多个拦截方法(也叫“陷阱函数”),每个拦截方法对应一种
- ◦ 若需要将操作传递给目标对象,可以通过 Reflect API(ES6内置,用于执行对象的默认操
- 12第 12 卷
- 13第 13 卷
- 14第 14 卷
- 15第 15 卷
- 16第 16 卷
- • 优点:嵌套转线性,可读性大幅提升;错误处理统一,避免遗漏;支持并行/串行控制(如
- (3)方案3:Generator + Promise + 自动执行器
- • 缺点:需要依赖第三方自动执行器(如 co);Generator函数理解成本较高;已被async/await替
- (4)方案4:async/await(最优方案)
- • 缺点:仅支持ES2017及以上环境,旧环境需通过Babel转译;本质基于Promise,需理解Promise
- (5)方案5:使用async库等第三方工具(早期方案)
- (1)Promise的定义
- • new Promise((resolve, reject) => {}):创建Promise实例,传入的执行器函数接收
- • promise.then(onFulfilled, onRejected):注册状态改变后的回调函数,返回新的
- • promise.catch(onRejected):专门注册失败回调,等价于 then(null,
- • Promise.all(iterable)、 Promise.race(iterable) 等:用于控制多个异步操作的并
- 17第 17 卷
- 18第 18 卷
- 19第 19 卷