JS
未读WeakMap & Map核心区别在于键类型、内存管理及可遍历性:
键类型限制:WeakMap 仅接受对象作为键(非对象如字符串、Symbol会报错),而Map支持任意类型键值。
弱引用机制:WeakMap的键是弱引用,若键对象无其他强引用,垃圾回收时自动清除对应条目,避免内存泄漏;Map强引用键,即使对象不再使用,仍保留条目导致内存无法释放。
可遍历性:WeakMap不可遍历(无size、keys()等方法),Map支持迭代器访问所有条目。
API差异:WeakMap仅有get、set、has、delete方法,Map额外提供clear、forEach及迭代接口。
应用场景对比:
WeakMap:适合关联对象与元数据且无需手动清理(如DOM元素绑定事件处理器,元素移除后自动释放)。
Map:需长期维护键值对或遍历操作的场景(如缓存数据、频率统计)。
示例:
12345678910// WeakMap:键为对象,自动清理 const wm = new WeakMap(); let obj = {}; wm.set(obj, 'data ...
JS
未读SymbolSymbol 是 JavaScript 中一种唯一且不可变的原始数据类型,其核心应用场景和内存管理特性如下:
使用场景
唯一属性键解决对象属性名冲突问题,确保第三方库扩展对象属性时互不影响:
12const LOG_LEVEL = Symbol('log');const config = { [LOG_LEVEL]: 'debug' }; // 避免与可能的现有属性冲突
模拟私有成员通过非枚举特性隐藏内部实现(需配合闭包):
12345678const _counter = Symbol('counter');class MyClass { constructor() { this[_counter] = 0; // 外部无法直接访问 } increment() { this[_counter]++; }}// 注:通过Object.getOwnPropertySymbols仍可访问,非绝对私有
内 ...
JS
未读深/浅拷贝深浅拷贝的核心区别在于是否递归复制对象的所有层级引用类型属性,常见实现方案如下:
浅拷贝实现
将原始对象或数组中的元素逐个复制到新对象或数组中
只会复制它们的引用,而不会创建这些嵌套对象或数组的新副本(改变原始对象会同时影响拷贝对象)
Object.assign()
12345const obj = { a: 1, b: { c: 2 } };const copy = Object.assign({}, obj);console.log(copy); // { a: 1, b: { c: 2 } }obj.b.c = 3;console.log(copy.b.c); // 输出: 3 (修改了原始对象的嵌套对象,影响到了浅拷贝)
扩展运算符(...)
12345const obj = { a: 1, b: { c: 2 } };const copy = { ...obj };console.log(c ...
JS
未读内存泄漏
不再使用的内存未被 GC 机制释放,导致 内存占用持续增长,最终可能引发性能问题或程序崩溃。
该问题只存在于启动的相关页面进程中,并不会永久性地占据电脑内存
一旦相关进程结束(关闭浏览器标签、重启浏览器或应用)操作系统会回收该进程占用的所有内存。
导致成因全局变量
意外出现的全局变量
全局变量不会被 GC 回收,直到页面关闭
123function leak() { leakedData = new Array(1000000); // 未使用 var/let/const,变量变为全局变量}
使用严格模式('use strict')避免隐式全局变量
闭包
闭包引用的外部变量会长期驻留内存
12345function createClosure() { const largeData = new Array(1000000); return () => console.log(largeData); // largeData 被闭包引用,会长期驻留内存}const closure = c ...
JS
未读垃圾回收机制(GC)核心思想垃圾回收器会定期检查堆内存的对象,判断哪些对象仍然被引用(“可达”)或不再被引用(“不可达”)
对于不可达的对象,垃圾回收器会将其占用的内存进行回收
可达:该对象可以通过作用域链、全局对象或其他对象的引用来访问
回收算法引用计数(已很少使用)每个对象都有一个引用计数器,记录有多少引用指向它
当引用计数为 0 时,表示该对象不可达,可以被回收
但无法处理循环引用问题
123456789function createCycle() { let obj1 = {}; let obj2 = {}; obj1.ref = obj2; // obj1 引用 obj2 obj2.ref = obj1; // obj2 引用 obj1 return [obj1, obj2];}let cycle = createCycle();cycle = null; // 即使外部没有引用,obj1 和 obj2 仍然互相引用
标记清除(最常用)垃圾回收器从根对象(如全局对象)开始,递归地标 ...
JS
未读V8引擎
是一个由 Google 开发的高性能 JavaScript 引擎
为了提高 Chrome 浏览器中的 JavaScript 执行速度而设计的
能够将 JavaScript 代码直接编译成原生机器码并执行,而不是解释执行
工作原理
解析(Parsing):当 JavaScript 代码被加载时,V8 首先将其解析为抽象语法树(AST)
Ignition(解释器):V8 使用名为 Ignition 的解释器来解释执行 AST
TurboFan(优化编译器):针对频繁执行的代码段,Ignition 会收集运行时信息,并将这些代码传递给 TurboFan 进行优化编译。TurboFan 能够生成高度优化的机器码,显著提高性能。
垃圾回收(Garbage Collection, GC):V8 实现了高效的内存管理机制,通过标记清除、分代收集等策略自动回收不再使用的对象,减少内存泄漏的风险。
特性
即时编译(JIT Compilation):不同于传统的解释型语言,V8 采用即时编译技术,在运行时动态地将字节码转换为机器码,以提升执行效率。
隐藏类(Hidden Classes):为了 ...
JS
未读栈/堆内存栈内存主要存储基本数据类型的值( Number、String、Boolean、Undefined、Null、Symbol 和 BigInt)
还存储变量的引用地址(指向堆内存的对象)
函数调用时的执行上下文也会压入栈中
内存是连续分配的,大小固定,内存的访问、分配、释放都非常快
123456789let a = 10; // 基本数据类型,存储在栈内存中let b = "Hello"; // 基本数据类型,存储在栈内存中let c = null; // 基本数据类型,存储在栈内存中function foo(x) { let y = 20; // 局部变量,存储在栈内存中 console.log(x + y);}foo(5); // 函数调用时,函数的执行上下文会被压入栈中
堆内存存储复杂数据类型的值(对象、数组、函数等)
还存储对象的属性、方法 & 数组的元素
内存非连续分配,大小不固定,分配和释放都慢,因为需要动态管理内存,而且需要通过引用地址间接访问
1234567let obj = { n ...
JS
未读闭包
函数 与其 词法作用域 的结合
当一个 内部函数 访问其外部函数作用域中的变量时,即使外部函数已经执行完毕,内部函数仍然保留对外部变量的引用。
闭包的形成是因为函数的作用域链(Scope Chain)在定义时就已经确定,而非执行时。
示例计数器1234567891011function createCounter() { let count = 0; // 当createCounter函数执行后本该被销毁 return { increment: () => count++, // 但函数保留了对count的引用 getCount: () => count, };}const counter = createCounter();counter.increment();console.log(counter.getCount()); // 1
函数柯里化
一种函数式编程技术,它将一个多参数函数转换为一系列单参数函数
柯里化可以逐步传递参数,而不是一次性传递所有参数
123456 ...
JS
未读事件循环
JS是单线程语言,同一时间只能执行一个任务
如果一个任务耗时过长(网络请求、文件读取),会阻塞后续代码的进行
同步任务:立刻放入JS引擎(JS主线程)执行,原地等待结果
异步任务:先放入宿主环境(浏览器/Node),不阻塞主线程,在将来执行
执行过程
过程中一共有三块环境:执行栈、宿主环境、任务队列
按代码上下文顺序
将同步任务压入执行栈
将异步任务压入宿主环境
在宿主环境中执行时机到来时(比如定时器到期),将宿主环境中异步任务(比如定时器)的回调函数推送给任务队列
此时执行栈会去看任务队列有没有异步任务需要执行,如果有则将异步任务推送到执行栈中执行
执行栈在同步任务执行完后会反复地去任务队列看有没有需要执行的异步任务
这个反复查找的过程就叫事件循环
宏任务/微任务
JS将 异步任务 分为宏任务和微任务
宏任务 由 宿主 发起
script (代码块)
setTimeout / setInterval 定时器
微任务 由 JS引擎 发起
promise (promise内部是同步,then/catch是回调异步)
Asy ...
JS
未读Proxy
允许你创建一个代理对象来拦截并自定义基本操作(属性查找、赋值、枚举、函数调用等)
怎么用1const proxy = new Proxy(target, handler);
target: 要代理的目标对象。
handler: 一个包含 get/set 等拦截方法的对象。
1234567891011121314151617let target = {};let handler = { get: function(obj, prop) { if (prop in obj) { console.log(`Getting ${prop}: ${obj[prop]}`); return obj[prop]; } else { console.log(`${prop} 属性不存在`); return undefined ...