2025前端面试JavaScript高频问题

96 阅读5分钟

JavaScript极简体系化面试题

image.png 以下若含有代码,是面试的代码阅读题,没对基本是必挂了

1. 数据类型

提问:JS中包含哪些数据类型?

  1. 基本数据类型:7个,包含Number、String、Boolean、Null、Undefined、Symbol、BigInt
  2. 引用数据类型:Object,包含(普通对象{}、Array、Date、Function)

常用类型判断

typeof x                 // 基本类型首选(注意 null)
Array.isArray(x)         // 数组
x instanceof Foo         // 原型链上是否有 Foo.prototype
Object.prototype.toString.call(x) // 最稳通用:"[object Date]" 等
Number.isNaN(x)          // 判断 NaN
Object.is(a, b)          // 比 === 更细:NaN、+0/-0

👉 注意点(面试常考)

  • typeof null === 'object'(历史遗留 Bug)
  • 基本类型 存储在栈内存 按值访问
  • 引用类型 存储在堆内存 ; 栈中保存的是变量的 引用地址

⭐ 变量存在哪个位置为什么重要,最直接的就是你需要知道深/浅拷贝的区别,然后手撕出来;深入后你也需要了解框架里面的浅比较比的是啥。

2. 声明方式

提问:ES6中包含的新特性--const、let和var的区别:

区别constletvar
作用域块级块级函数
变量提升无(暂时性死区)无(暂时性死区)
是否可重复声明❌ 不可❌ 不可✅ 可
是否可重新赋值❌ 不可(地址不可变,属性值可变)✅ 可✅ 可
---

题目:答案在最后

for (var i = 0; i < 3; i++) { 
  setTimeout(() => {
    console.log(i)
  }, 0)
}

✅ 输出:3 3 3

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 0)
}

✅ 输出:0 1 2

3. 闭包(涉及作用域)

什么是闭包: 闭包是:函数 + 定义时的词法作用域

面试回答: 当一个函数访问了其外部函数的变量,并且这个函数被返回或在外部执行,就形成了闭包。

闭包的作用:

  • 数据私有化
  • 保存变量状态
  • 实现模块化

闭包的缺点:

  • 内存不能及时释放
  • 使用不当可能造成内存泄漏(然后官就要问你了解啥是内存泄漏不?(标记+清除)) ->

题目:

4. 原型链

什么是原型?

  • 每个函数都有一个 prototype 属性
  • 每个对象都有一个 __proto__ / Object.getPrototypeOf 指向其构造函数的 prototype

什么是原型链? 当访问一个对象的属性时,
如果对象自身没有该属性,就会沿着 __proto__ 指向的原型对象查找,
直到找到为止,或者查找到 null 为止,这条查找路径称为 原型链

function Foo() {}
const f = new Foo()

Foo.prototype === f.__proto__        // ?
f.__proto__.__proto__ === Object.prototype // ?

✅ 结果:Foo.prototype === f.__proto__true f.__proto__.__proto__ === Object.prototypetrue

5. 继承:

提问:JS有几种继承方式?(中低频,因为官认为上面的都太简单了)

  • 1️⃣ 原型链继承
  • 2️⃣ 构造函数继承(借用构造函数)
  • 3️⃣ 组合继承(最经典)
  • 4️⃣ 原型式继承
  • 5️⃣ 寄生组合继承(最优解) ⭐
  • 6️⃣ ES6 class 继承(语法糖)(基于原型链实现的,只是语法更清晰)

6. JS是如何执行的?

1️⃣ 源码 → AST(抽象语法树)
2️⃣ AST → 字节码 / 机器码(JIT 编译)引擎生成字节码,热点代码可能被JIT优化成机器码
3️⃣ 运行时创建 执行上下文(Execution Context) ,压入调用栈

执行上下文包括:
  • Lexical Environment(词法环境)let/const、块级绑定、TDZ

  • Variable Environment(变量环境)var、函数声明等

  • Scope Chain(作用域链)

  • this 绑定(由调用方式决定;箭头函数是词法捕获)

this 绑定(补一段超高频)
  • fn():严格模式 undefined,非严格是 globalThis(浏览器是 window
  • obj.fn():this 指向 obj
  • new Fn():this 指向新实例
  • call/apply/bind:显式绑定(bind 返回新函数)
  • 箭头函数:没有自己的 this,this 来自定义时外层作用域(与调用无关)

JS 执行模型

  • 单线程
  • 事件循环(Event Loop)
    • 任务队列: 同步任务 & 异步任务(宏任务 / 微任务)
    • 执行顺序(浏览器典型)
      1. 执行一段同步代码(一个宏任务)
      2. 清空所有微任务队列
      3. 取下一个宏任务……
        (循环往复)

题目:

//最简单的
console.log(1)

setTimeout(() => {
  console.log(2)
}, 0)

Promise.resolve().then(() => {
  console.log(3)
})

console.log(4)

✅ 输出:1 4 3 2

console.log(1);

setTimeout(() => console.log(2), 0);

(async () => {
  console.log(5);
  await new Promise((r) => setTimeout(r, 0)); // 等一个宏任务
  console.log(6);
})();

Promise.resolve().then(() => console.log(3));

console.log(4);
//最难的(有循环引用 
let p; //一旦 then 的回调返回的值“最终等于它要 resolve 的那个 promise 自己” ,这个 promise 必须立刻 reject(抛出 `TypeError`),
p = Promise.resolve()
  .then(() => {
    return p;   // 返回 p 本身
  })
  .then(() => {
    console.log("B"); // 不会执行
  })
  .catch(() => {
    console.log("C"); // 会执行
  });

✅ 结果:只会打印 "C"(不会打印 "B"

//包含node的事件循环  `process.nextTick`(优先于 Promise 微任务)
console.log(1);

setTimeout(() => console.log(2), 0);

Promise.resolve().then(() => console.log(3));

process.nextTick(() => console.log("N")); // Node nextTick 队列

(async () => {
  console.log(5);
  await 0;        // 等价 Promise.resolve(0),续写进 Promise 微任务队列
  console.log(6);
})();

console.log(4);

✅在 Node 中典型输出:1 5 4 N 3 6 2

拓展:React的tsx是如何渲染成DOM的?VUE是如何变成真实DOM的?

React(JSX / TSX → DOM)

  1. 编译阶段

JSX/TSX 会被编译成 React Element 的创建表达式

-   旧:`React.createElement(...)`
-   新(automatic runtime):`jsx/jsxs`(不一定显式出现 createElement)

2. Render 阶段(构建 Fiber 树)

-   React 根据 Element 生成/更新 **Fiber Tree**

-**Reconciliation(协调)** :对比新旧 Fiber,计算需要的更新(diff)

-   React 的 diff 关键点:

    -   同层比较(按层级)
    -   列表靠 `key` 辅助复用/移动(没有 key 会导致性能/状态问题)

3. Commit 阶段(真正改 DOM)

-   把计算好的变更提交到宿主环境(浏览器 DOM)
-   执行生命周期/副作用(如 `useEffect` 在 commit 后调度)

4. 调度与更新(React 18 常被问)

-   更新会被调度(Scheduler),可能进行批处理(batching)
-   并发特性(Concurrent)让渲染可被中断/恢复(概念上,不必展开太深)

Vue(Template → DOM)

  1. 编译阶段(SFC / Template)

     -  Template 会被编译成 `render()` 函数(内部调用 `h` 创建 VNode)
     -  Vue 3 编译器会做很多优化:
         -   静态提升(static hoist)
         -   Patch Flags(标记动态节点)
         -   Block Tree(减少 diff 范围)
    
  2. 响应式系统驱动更新

     -   Vue 3 用 `Proxy`(Vue 2 用 `Object.defineProperty` -   组件渲染时会“依赖收集”:读取到的响应式数据会被 track
     -   数据变更时触发 trigger,把对应组件的更新放进调度队列
    
  3. 渲染与 Patch

     -   `render()` 生成 VNode
     -   runtime 做 patch(对比新旧 VNode)
     -   最终更新真实 DOM
     -   `nextTick` 本质是等待这轮 DOM patch 完成后的回调(通常基于微任务调度)
    

两种框架本质都是相同的,都是Virtual DOM + Diff 。