首页/文章/八股文

堆栈/异步/执行栈连环问

2025-08-07
25299 分钟
...

1. 数据类型分别存在哪里?

(1) var a = {name: "前端开发"}; var b = a; a = null,b输出什么?

var a = {name: "前端开发"}; 
var b = a;                 
a = null;                  
console.log(b);            

关键点b = a地址复制,不是深拷贝。a断开连接,b不受影响。

「Q1: 如果 b.name = "改了",a再访问会怎样?❓」
A1: a已为null,但若a未被赋null,a.name也会变成"改了",因为指向同一堆对象!👉继续看?

「Q2: 什么情况下b会变null?💥」
A2: 只有 b = null 才行。或者全局作用域被销毁(如页面关闭)。

「Q3: 这种机制叫什么?🤯」
A3: 按引用传递(pass-by-reference)的错觉,其实是按值传递地址(call-by-sharing)。


(2) var a = {b: 1} 存放在哪里?

  • a 这个变量名:栈中
  • {b: 1} 整个对象:堆中
  • a 的值:指向堆中该对象的指针(内存地址)

ASCII示意:

(Stack)        (Heap)
+-------+       +-------------+
|   a   | ----> | {b: 1}      |
+-------+       +-------------+

⚠️你以为a存的是{b:1}?不,它只存了个“门牌号”!


(3) var a = {b: {c: 1}} 存放在哪里?

ASCII示意:


+-------+       +-------------+
|   a   | ----> | b: ──────┐  |
+-------+       +----------|--

                   +-------------+
                   | c: 1        |另一个堆对象
                   +-------------+

⚠️每层对象都是独立堆内存块!

「Q4: 深拷贝为什么慢?💥」
A4: 因为要递归遍历所有嵌套对象,在堆中新建副本,还要重建指针关系。

「Q5: WeakMap的key为什么只能是对象?🤯」
A5: 因为它用弱引用指向堆对象,不影响垃圾回收,基本类型无法弱引用。


2. 栈和堆的区别?

维度栈(Stack)堆(Heap)
管理方式自动管理(LIFO)手动/GC管理
速度快(连续内存)慢(碎片化)
生命周期与函数执行周期绑定动态,直到无引用
存储内容基本类型、函数参数、局部变量、执行上下文对象、数组、闭包环境
内存分配编译时确定大小运行时动态分配

「Q6: 递归太深为什么会栈溢出?💥」
A6: 每次调用函数都会在栈压入执行上下文,超出内存限制 → Maximum call stack size exceeded

「Q7: 堆内存一定比栈大吗?❓」
A7: 通常如此,但取决于引擎实现和系统资源。


3. 垃圾回收时栈和堆的区别?

  • :函数执行完自动弹出,变量自动销毁,无需GC介入 ✅
  • :需GC(如V8的分代回收)标记-清除/引用计数来回收无用对象
function foo() {
  var obj = {a: 1}; 
}
foo(); 

「Q8: 闭包为什么会导致内存泄漏?⚠️」
A8: 内层函数引用外层变量,外层执行完但变量仍被引用 → 堆对象无法回收!

「Q9: WeakSet如何避免内存泄漏?👉继续看?」
A9: 它的元素是弱引用,不影响GC,适合临时存储对象元数据。


4. 数组取第1个和第10万个元素时间差?

几乎无差别!O(1)

因为数组是连续内存块,通过基地址 + 索引 × 元素大小直接计算位置。

address = baseAddress + index * sizeof(element)

无论 index=0 还是 index=99999,计算一步到位。

「Q10: 什么数据结构查第n个元素是O(n)?💥」
A10: 链表!必须从头逐个遍历。

「Q11: JS数组真是连续内存吗?❓」
A11: 对密集数组(数值索引连续),V8会优化为连续存储;稀疏数组用哈希表。


5. 栈和堆具体怎么存储?

栈存储结构(函数调用栈)

+------------------+
| foo() 执行上下文  |
| - 变量对象        |
| - this           |
| - 作用域链        |
+------------------+
| bar() 上下文      |
+------------------+
| global 上下文     |
+------------------+

LIFO,函数调用即压栈,return即弹栈。

堆存储结构

  • 对象以键值对+隐藏类(Hidden Class) 存储,V8用指针指向属性位置
  • 数组可能用快速属性(in-object)或慢速字典模式

「Q12: 为什么频繁增删对象属性会变慢?🤯」
A12: 因为会破坏隐藏类,V8无法做内联缓存优化 → 回退到字典查找。


6. JS怎么实现异步?

事件循环(Event Loop) + 回调队列(Callback Queue)

⚡异步不是JS干的,是浏览器/Node帮它托底!

 

「Q13: Node和浏览器的Event Loop一样吗?💥」
A13: 不同!Node有多个阶段(timers, poll, check等),浏览器更简单。


7. 异步整个执行周期?

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);

执行流程:

  1. 同步代码入栈执行 → 1, 4
  2. setTimeout → 丢给Web API,到期后进宏任务队列
  3. Promise.then → 进微任务队列
  4. 同步执行完 → 清空微任务(3)→ 下一轮事件循环取宏任务(2)

「Q14: 微任务一定比宏任务先执行吗?👉继续看?」
A14: 是!每轮事件循环末尾都会清空微任务队列。


8. Async/Await怎么实现?

语法糖 + Promise + 状态机

async function fn() {
  const res = await fetch('/api');
  return res.json();
}

function fn() {
  return Promise.resolve(fetch('/api')).then(res => {
    return res.json();
  });
}

Babel转译后本质是 generator + co 模式,自动管理Promise状态。

「Q15: await后面不是Promise会怎样?💥」
A15: 会被 Promise.resolve() 包装,立即resolve。


9. Promise和setTimeout执行先后?

setTimeout(() => console.log('宏'), 0);
Promise.resolve().then(() => console.log('微'));

  • Promise.then → 微任务
  • setTimeout → 宏任务

微任务优先于宏任务执行

「Q16: 为什么要有微任务?直接都放宏任务不行吗?🤯」
A16: 为了保证异步回调能“同步”执行完,比如Promise链,避免中间被其他宏任务打断。


10. 为什么要区分微任务和宏任务?

为了执行优先级与一致性

  • 微任务:当前操作的延续(如Promise链、MutationObserver)
  • 宏任务:独立事件(如setTimeout、I/O、UI渲染)

想象你写代码:then().then().then() 应该一口气执行完,而不是被中间插入一个setTimeout打断。

「Q17: Vue的$nextTick用了哪种任务?💥」
A17: 优先微任务(Promise.then),降级为宏任务(setTimeout)。


11. Promise构造函数是同步还是异步?then呢?

new Promise((resolve) => {
  console.log('构造函数立即执行'); 
  resolve(1);
}).then(res => {
  console.log('then是异步', res); 
});

  • 构造函数:同步执行
  • then回调:异步(微任务)

「Q18: catch是同步还是异步?👉继续看?」
A18: 同样是微任务,和then一样异步执行。


12. 发布-订阅 vs 观察者模式?

| | 观察者模式(Observer) | 发布-订阅(Pub-Sub) | | --- | --- | --- | --- | | 耦合度 | 高(目标直接管理观察者) | 低(通过事件中心解耦) | | 通信方式 | 目标 → 观察者 | 发布者 ⇄ 事件中心 ⇄ 订阅者 | | 角色 | Subject + Observer | Publisher + Broker + Subscriber | ```js |

class Subject { observers = []; addObserver(o) { this.observers.push(o); } notify() { this.observers.forEach(o => o.update()); } }

```js

const events = {};
on('click', handler);
emit('click'); 

「Q19: Vue2的响应式是哪种?💥」
A19: 观察者模式!Dep是Subject,Watcher是Observer。


13. JS执行过程分哪些阶段?

  1. 语法分析:检查代码是否合法
  2. 预编译:创建GO/AO,变量提升
  3. 执行
    • 创建执行上下文(入栈)
    • this绑定
    • 词法环境初始化
    • 代码执行
    • 上下文出栈

「Q20: let/const为什么有暂时性死区?🤯」
A20: 因为预编译时也提升,但不初始化,直到声明语句才初始化 → 中间区域访问报错。


14. 词法作用域和this的区别?

| | 词法作用域(Lexical Scope) | this(动态绑定) | | --- | --- | --- | --- | | 确定时机 | 函数定义时(写代码时) | 函数调用时(运行时) | | 决定因素 | 函数在哪定义 | 如何调用(谁调用、new、call等) | | 查找方式 | 向外层作用域链查找 | 根据调用方式动态绑定 | ```js |

var name = ‘window’; function foo() { console.log(this.name);
console.log(name);
} const obj = {name: ‘obj’, foo}; foo();
obj.foo();

Q21: 箭头函数的this是词法作用域吗?💥」  
A21: 箭头函数没有自己的this继承外层函数定义时的作用域

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2025-10-13
cookie跨域介绍
Cookie 跨域问题详解 在 Web 开发中, Cookie 是最常见的客户端存储机制之一,用于记录用户登录状态、偏好设置、会话信息等。然而,当涉及到 跨域请求 时,Cookie 的行为往往变得复杂,尤其在现代浏览器的安全策略下,跨域 Cookie 的传递和写入都有严格的限制。 本文将从基础概念开始,逐步讲解 Cookie 的跨域机制、SameSite 属性、CORS 配置及常见问题解决方案。 一、Cookie 的基础概念 Cooki...
学习面试
八股文2025-10-09
JavaScript 闭包详解
JavaScript 闭包(Closure)详解 一、什么是闭包 闭包(Closure) 是 JavaScript 中一个非常核心且常被问到的概念。简单来说: 闭包是一个函数,它可以“记住”并访问其定义时所在的词法作用域,即使这个函数在其作用域之外被调用。 换句话说,当一个函数“嵌套”在另一个函数中,并且 内部函数引用了外部函数的变量 时,就形成了闭包。 示例: 虽然 已经执行完毕,但 依然可以访问 中的 。这就是闭包。 二、闭包的形成...
学习面试
八股文2025-10-07
开始性能优化之旅
事件循环机制 一、JavaScript 引擎的本质 核心职责 : 解析 JavaScript 语法 管理变量和内存 执行代码逻辑 不涉及 : 线程管理(Worker除外) I/O 操作 定时器控制 网络请求 常见引擎:V8(Chrome)、SpiderMonkey(Firefox)、JavaScriptCore(Safari) 二、宿主环境的扩展能力 宿主提供的多线程能力 : | 线程类型 | 功能 | 对应 API | | | | |...
学习面试
八股文2025-09-30
前端首屏优化
话说,我在面试的时候,80%的情况下,都会被问到首屏优化问题,烦,恨 TCP Slow Start(慢启动)概念 咱先不说标题的数字哪里来的,先说一个概念,就是TCP的慢启动。 你们想哈,在浏览器和服务器开始建立连接的时候,服务器是并不知道浏览端网络的带宽、拥塞状况。假设你本地的带宽是1M,如果一开始服务器就发送2M的文件,那浏览器压根就不是人类,忍忍或者挤挤就能接收到的,这样就可能会引发丢包和重传。 想想,这个该有什么办法解决这个问题...
学习面试
八股文2025-09-28
Function.prototype和Object.prototype
一道面试题引发的原型链血案,彻底搞懂 Function.prototype 和 Object.prototype 前言 有这样一道题: 目录 问题分析与答案揭晓 让我们先公布正确答案,然后深入分析原因: 为什么 是 而不是 ? 这是因为函数和实例的原型链路径完全不同! 原型链的本质机制 JavaScript中的两条原型链 在JavaScript中,存在两条不同的原型链:       关键理解:F.proto...
学习面试
八股文2025-09-10
让你彻底明白什么是闭包
今天我们来聊一个听起来很高大上,但实际上你可能天天在用(只是不知道它名字)的概念—— 闭包 。 一、一个你肯定写过的闭包 先别管定义,来看这段代码,你是不是再熟悉不过了? 恭喜你!这就是一个经典的闭包!是不是很简单? 二、为什么会有闭包?—— 背包的故事 想象一下,JavaScript 中的函数就像一个小机器人,当它被创建时,会背着一个神奇的 背包 。 这个背包里装着什么呢?装着它 出生时 所在环境的所有变量! 当我们调用 时,返回的那...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录