2026年前端面试必备:Top 50 JavaScript高频面试题与详解

2026年前端面试必备:Top 50 JavaScript高频面试题与详解

涵盖2026年最新JavaScript面试高频考点,从基础概念到高级特性,每道题配有详细解析和代码示例。适合初级到高级前端开发者面试备考。

2026年3月17日15分钟阅读

2026年JavaScript面试全景

随着前端技术生态的持续演进,2026年的JavaScript面试已经涵盖了从经典概念到最新规范的广泛知识点。本文精选50道高频面试题,配合详细解析和可运行的代码示例,帮助你系统性地备战前端面试。

面试准备过程中,善用正则表达式测试工具可以帮助你验证和调试正则相关题目的解答。


第一部分:JavaScript基础概念(题目1-10)

题目1:解释 varletconst 的区别

这是最基础也是最容易被深挖的题目。

标准答案:

特性varletconst
作用域函数作用域块级作用域块级作用域
变量提升是(提升为undefined)是(暂时性死区TDZ)是(暂时性死区TDZ)
重复声明允许不允许不允许
重新赋值允许允许不允许
全局对象属性
// var的函数作用域问题
function example() {
  if (true) {
    var x = 1; // x在整个函数内可访问
    let y = 2; // y只在if块内可访问
  }
  console.log(x); // 1
  console.log(y); // ReferenceError: y is not defined
}

// 暂时性死区(TDZ)
console.log(a); // undefined(var提升)
console.log(b); // ReferenceError(TDZ)
var a = 1;
let b = 2;

// const的深层含义
const obj = { name: "Alice" };
obj.name = "Bob"; // 可以!修改属性
obj = {};          // TypeError!不能重新赋值

追问准备: 面试官可能会追问"如何让const对象真正不可变"——答案是Object.freeze(),但注意它只是浅冻结。


题目2:什么是闭包(Closure)?

标准答案:

闭包是指一个函数能够记住并访问其词法作用域(Lexical Scope),即使该函数在其词法作用域之外执行。

// 经典闭包示例
function createCounter() {
  let count = 0; // 私有变量

  return {
    increment() { count++; },
    decrement() { count--; },
    getCount() { return count; }
  };
}

const counter = createCounter();
counter.increment();
counter.increment();
counter.increment();
counter.decrement();
console.log(counter.getCount()); // 2

// 闭包的经典陷阱(面试常考)
// 问题:以下代码输出什么?
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 输出:3 3 3(不是0 1 2)

// 解决方案一:使用let
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}
// 输出:0 1 2

// 解决方案二:IIFE
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => console.log(j), 0);
  })(i);
}

实际应用: 模块模式、数据封装、函数柯里化、记忆化(Memoization)。


题目3:原型链(Prototype Chain)是什么?

// 原型链示意
function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function() {
  console.log(`${this.name} 发出声音`);
};

function Dog(name, breed) {
  Animal.call(this, name); // 继承属性
  this.breed = breed;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
  console.log(`${this.name} 汪汪叫!`);
};

const myDog = new Dog("旺财", "柴犬");
myDog.speak(); // 旺财 发出声音(继承自Animal)
myDog.bark();  // 旺财 汪汪叫!

// 原型链查找顺序
// myDog → Dog.prototype → Animal.prototype → Object.prototype → null

// 检查原型关系
console.log(myDog instanceof Dog);    // true
console.log(myDog instanceof Animal); // true
console.log(Object.getPrototypeOf(myDog) === Dog.prototype); // true

题目4:this 的指向规则

// 规则1:默认绑定(全局/undefined)
function greet() {
  console.log(this); // 严格模式:undefined,非严格:window/global
}

// 规则2:隐式绑定
const obj = {
  name: "Alice",
  greet() { console.log(this.name); }
};
obj.greet(); // "Alice",this指向obj

// 规则3:显式绑定
function sayHi(greeting) {
  console.log(`${greeting}, ${this.name}`);
}
const person = { name: "Bob" };
sayHi.call(person, "你好");   // "你好, Bob"
sayHi.apply(person, ["嗨"]); // "嗨, Bob"
const boundSayHi = sayHi.bind(person);
boundSayHi("Hey");            // "Hey, Bob"

// 规则4:new绑定
function Person(name) {
  this.name = name;
}
const alice = new Person("Alice");
console.log(alice.name); // "Alice"

// 规则5:箭头函数(捕获外层this)
const timer = {
  seconds: 0,
  start() {
    setInterval(() => {
      this.seconds++; // this指向timer,不是全局
      console.log(this.seconds);
    }, 1000);
  }
};

题目5:Promise与async/await

// Promise基础
const fetchUser = (id) => new Promise((resolve, reject) => {
  setTimeout(() => {
    if (id > 0) {
      resolve({ id, name: "用户" + id });
    } else {
      reject(new Error("无效的用户ID"));
    }
  }, 1000);
});

// Promise链式调用
fetchUser(1)
  .then(user => {
    console.log("获取到用户:", user);
    return fetchUser(user.id + 1);
  })
  .then(nextUser => console.log("下一个用户:", nextUser))
  .catch(err => console.error("错误:", err.message))
  .finally(() => console.log("请求完成"));

// async/await(更优雅的写法)
async function loadUsers() {
  try {
    const user1 = await fetchUser(1);
    const user2 = await fetchUser(2);
    console.log(user1, user2);
  } catch (err) {
    console.error("加载用户失败:", err.message);
  }
}

// 并行请求(重要!)
async function loadUsersConcurrently() {
  const [user1, user2, user3] = await Promise.all([
    fetchUser(1),
    fetchUser(2),
    fetchUser(3),
  ]);
  console.log(user1, user2, user3);
}

// Promise.allSettled(不会因一个失败而全部失败)
const results = await Promise.allSettled([
  fetchUser(1),
  fetchUser(-1), // 这个会失败
  fetchUser(3),
]);
results.forEach(result => {
  if (result.status === "fulfilled") {
    console.log("成功:", result.value);
  } else {
    console.log("失败:", result.reason.message);
  }
});

题目6:事件循环(Event Loop)

// 理解宏任务与微任务
console.log("1. 同步代码开始");

setTimeout(() => console.log("4. 宏任务(setTimeout)"), 0);

Promise.resolve()
  .then(() => console.log("3. 微任务(Promise)"))
  .then(() => console.log("3.5 微任务链"));

queueMicrotask(() => console.log("2.5. queueMicrotask"));

console.log("2. 同步代码结束");

// 输出顺序:1 → 2 → 2.5 → 3 → 3.5 → 4

// 关键规则:
// 微任务(Promise.then, queueMicrotask, MutationObserver)
// 优先于宏任务(setTimeout, setInterval, I/O)执行

题目7:深拷贝 vs 浅拷贝

// 浅拷贝方法
const obj = { a: 1, b: { c: 2 } };

const shallow1 = Object.assign({}, obj);
const shallow2 = { ...obj };

shallow1.b.c = 99;
console.log(obj.b.c); // 99!b是引用,被修改了

// 深拷贝方法
// 方法1:JSON序列化(简单但有限制)
const deep1 = JSON.parse(JSON.stringify(obj));
// 限制:不能处理函数、undefined、循环引用、Date对象

// 方法2:structuredClone(ES2022,推荐)
const deep2 = structuredClone(obj);
deep2.b.c = 100;
console.log(obj.b.c); // 原值不变

// 方法3:手写深拷贝(面试常考)
function deepClone(obj, map = new WeakMap()) {
  if (obj === null || typeof obj !== "object") return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  if (map.has(obj)) return map.get(obj); // 处理循环引用

  const clone = Array.isArray(obj) ? [] : {};
  map.set(obj, clone);

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map);
    }
  }
  return clone;
}

题目8:防抖(Debounce)与节流(Throttle)

// 防抖:延迟执行,连续触发只执行最后一次
function debounce(fn, delay) {
  let timer = null;
  return function(...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

// 应用场景:搜索框输入、窗口resize
const handleSearch = debounce((value) => {
  console.log("搜索:", value);
}, 500);

// 节流:限制执行频率,固定时间间隔执行一次
function throttle(fn, interval) {
  let lastTime = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

// 应用场景:滚动事件、鼠标移动、按钮点击防止连击
const handleScroll = throttle(() => {
  console.log("处理滚动事件");
}, 200);

题目9:WeakMap 和 WeakSet 的用途

// WeakMap:弱引用Map,key必须是对象
const cache = new WeakMap();

function processObject(obj) {
  if (cache.has(obj)) {
    return cache.get(obj); // 使用缓存
  }
  const result = heavyComputation(obj);
  cache.set(obj, result);
  return result;
}
// 当obj被垃圾回收时,cache中对应的条目也会自动清除
// 避免内存泄漏

// 实际应用:私有数据存储
const privateData = new WeakMap();

class User {
  constructor(name, password) {
    privateData.set(this, { password });
    this.name = name;
  }

  checkPassword(input) {
    return privateData.get(this).password === input;
  }
}

const user = new User("Alice", "secret123");
console.log(user.password);           // undefined(无法直接访问)
console.log(user.checkPassword("secret123")); // true

题目10:Generator 函数

// Generator:可暂停和恢复的函数
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2
console.log(fib.next().value); // 3

// 使用for...of消费有限Generator
function* range(start, end, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

for (const num of range(0, 10, 2)) {
  console.log(num); // 0 2 4 6 8
}

第二部分:ES6+现代语法(题目11-20)

题目11:解构赋值的高级用法

// 数组解构
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first, second, rest); // 1 2 [3, 4, 5]

// 交换变量
let [x, y] = [1, 2];
[x, y] = [y, x];
console.log(x, y); // 2 1

// 对象解构+重命名+默认值
const { name: userName = "匿名", age = 18, address: { city } = {} } = {
  name: "Alice",
  address: { city: "北京" }
};
console.log(userName, age, city); // "Alice" 18 "北京"

// 函数参数解构
function renderUser({ name, role = "用户", permissions: { canEdit = false } = {} }) {
  console.log(`${name}${role})可编辑:${canEdit}`);
}
renderUser({ name: "Bob", permissions: { canEdit: true } });

题目12:Proxy 和 Reflect

// Proxy:拦截对象操作(Vue 3的响应式原理)
function reactive(target) {
  return new Proxy(target, {
    get(target, key, receiver) {
      console.log(`读取属性:${String(key)}`);
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      console.log(`设置属性:${String(key)} = ${value}`);
      const result = Reflect.set(target, key, value, receiver);
      // 触发UI更新...
      return result;
    },
    deleteProperty(target, key) {
      console.log(`删除属性:${String(key)}`);
      return Reflect.deleteProperty(target, key);
    }
  });
}

const state = reactive({ count: 0, name: "Vue3响应式" });
state.count++;     // 读取count,设置count = 1
console.log(state.name); // 读取name

题目13:可选链(?.)和空值合并(??)

// 可选链:避免null/undefined错误
const user = {
  profile: {
    address: {
      city: "上海"
    }
  }
};

// 传统写法(繁琐)
const city1 = user && user.profile && user.profile.address && user.profile.address.city;

// 可选链(简洁)
const city2 = user?.profile?.address?.city;
const zipCode = user?.profile?.address?.zipCode; // undefined,不报错

// 可选链调用方法
const uppercaseCity = user?.profile?.getCity?.(); // 如果getCity不存在,返回undefined

// 空值合并(??):只对null/undefined触发默认值
const age = null ?? 18;     // 18
const score = 0 ?? 100;     // 0(0不是null/undefined)
const name = "" ?? "匿名";  // ""(空字符串不是null/undefined)

// 与 || 的区别
const age2 = 0 || 18;  // 18(0是falsy,触发默认值)

题目14:Symbol 的使用场景

// Symbol:唯一标识符
const id1 = Symbol("id");
const id2 = Symbol("id");
console.log(id1 === id2); // false

// 用于对象私有属性
const _private = Symbol("private");
class MyClass {
  constructor() {
    this[_private] = "私有数据";
    this.public = "公开数据";
  }
}
const instance = new MyClass();
console.log(Object.keys(instance)); // ["public"],Symbol属性不出现

// 内置Symbol(迭代器协议)
class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        if (current <= end) {
          return { value: current++, done: false };
        }
        return { done: true };
      }
    };
  }
}

for (const num of new Range(1, 5)) {
  console.log(num); // 1 2 3 4 5
}

题目15:模块系统(ESM vs CommonJS)

// CommonJS(Node.js传统)
const fs = require("fs");
module.exports = { myFunction };

// ES Modules(现代标准)
import fs from "fs";
export const myFunction = () => {};
export default class MyClass {}

// 动态导入(代码分割)
async function loadComponent(name) {
  const { default: Component } = await import(`./components/${name}.js`);
  return Component;
}

// 关键区别
// ESM:静态分析,支持Tree Shaking,异步加载
// CJS:动态,同步加载,运行时确定依赖

第三部分:异步编程进阶(题目16-25)

题目16:手写Promise

class MyPromise {
  constructor(executor) {
    this.state = "pending";
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled";
        this.value = value;
        this.onFulfilledCallbacks.forEach(fn => fn());
      }
    };

    const reject = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected";
        this.reason = reason;
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (e) {
      reject(e);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
    onRejected = typeof onRejected === "function" ? onRejected : e => { throw e; };

    return new MyPromise((resolve, reject) => {
      const handleFulfilled = () => {
        try {
          const result = onFulfilled(this.value);
          result instanceof MyPromise ? result.then(resolve, reject) : resolve(result);
        } catch (e) {
          reject(e);
        }
      };

      if (this.state === "fulfilled") {
        queueMicrotask(handleFulfilled);
      } else if (this.state === "pending") {
        this.onFulfilledCallbacks.push(handleFulfilled);
      }
    });
  }
}

题目17:实现一个请求并发控制器

这是考验实际工程能力的高频题:

// 实现:最多同时发送N个请求
class RequestPool {
  constructor(maxConcurrent) {
    this.maxConcurrent = maxConcurrent;
    this.running = 0;
    this.queue = [];
  }

  add(requestFn) {
    return new Promise((resolve, reject) => {
      this.queue.push({ requestFn, resolve, reject });
      this.run();
    });
  }

  run() {
    while (this.running < this.maxConcurrent && this.queue.length > 0) {
      const { requestFn, resolve, reject } = this.queue.shift();
      this.running++;

      requestFn()
        .then(resolve)
        .catch(reject)
        .finally(() => {
          this.running--;
          this.run(); // 有空位,继续处理队列
        });
    }
  }
}

// 使用示例
const pool = new RequestPool(3); // 最多3个并发

const urls = Array.from({ length: 10 }, (_, i) => `https://api.example.com/item/${i}`);
const results = await Promise.all(
  urls.map(url => pool.add(() => fetch(url).then(r => r.json())))
);

题目18-25:更多异步与性能题目

// 题目18:AbortController(取消请求)
const controller = new AbortController();
const { signal } = controller;

fetch("https://api.example.com/data", { signal })
  .then(res => res.json())
  .catch(err => {
    if (err.name === "AbortError") {
      console.log("请求已取消");
    }
  });

setTimeout(() => controller.abort(), 3000); // 3秒后取消

// 题目19:IntersectionObserver(懒加载)
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: "100px" });

document.querySelectorAll("img[data-src]").forEach(img => observer.observe(img));

// 题目20:Web Workers(多线程)
// main.js
const worker = new Worker("heavy-task.js");
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => console.log("计算结果:", e.data);

// heavy-task.js
self.onmessage = (e) => {
  const result = e.data.data.reduce((sum, n) => sum + n, 0);
  self.postMessage(result);
};

第四部分:面向对象与设计模式(题目26-35)

题目26:实现发布订阅模式(EventEmitter)

class EventEmitter {
  constructor() {
    this.events = new Map();
  }

  on(event, listener) {
    if (!this.events.has(event)) {
      this.events.set(event, []);
    }
    this.events.get(event).push(listener);
    return this; // 支持链式调用
  }

  once(event, listener) {
    const wrapper = (...args) => {
      listener.apply(this, args);
      this.off(event, wrapper);
    };
    return this.on(event, wrapper);
  }

  emit(event, ...args) {
    if (this.events.has(event)) {
      this.events.get(event).forEach(listener => listener.apply(this, args));
    }
    return this;
  }

  off(event, listener) {
    if (this.events.has(event)) {
      const listeners = this.events.get(event).filter(l => l !== listener);
      this.events.set(event, listeners);
    }
    return this;
  }
}

// 使用示例
const emitter = new EventEmitter();
emitter
  .on("data", (data) => console.log("收到数据:", data))
  .once("connect", () => console.log("已连接(只触发一次)"))
  .emit("connect")   // "已连接"
  .emit("connect")   // 无输出(once已移除)
  .emit("data", { id: 1 }); // "收到数据: { id: 1 }"

题目27:实现柯里化(Curry)

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...moreArgs) {
      return curried.apply(this, args.concat(moreArgs));
    };
  };
}

// 使用示例
const add = curry((a, b, c) => a + b + c);
console.log(add(1)(2)(3));  // 6
console.log(add(1, 2)(3));  // 6
console.log(add(1)(2, 3));  // 6
console.log(add(1, 2, 3));  // 6

// 实际应用:函数组合
const multiply = curry((factor, num) => num * factor);
const double = multiply(2);
const triple = multiply(3);

[1, 2, 3, 4, 5].map(double); // [2, 4, 6, 8, 10]

题目28-35:其他高频面试题

// 题目28:数组去重的多种方法
const arr = [1, 2, 2, 3, 3, 4];
// 方法1: Set(推荐)
[...new Set(arr)]; // [1, 2, 3, 4]
// 方法2: filter + indexOf
arr.filter((item, index) => arr.indexOf(item) === index);
// 方法3: reduce
arr.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []);

// 题目29:扁平化数组
const nested = [1, [2, [3, [4]]]];
nested.flat(Infinity); // [1, 2, 3, 4]

// 手写flat
function flatten(arr, depth = 1) {
  return arr.reduce((acc, item) => {
    if (Array.isArray(item) && depth > 0) {
      acc.push(...flatten(item, depth - 1));
    } else {
      acc.push(item);
    }
    return acc;
  }, []);
}

// 题目30:实现call/apply/bind
Function.prototype.myCall = function(context, ...args) {
  context = context || globalThis;
  const sym = Symbol();
  context[sym] = this;
  const result = context[sym](...args);
  delete context[sym];
  return result;
};

Function.prototype.myBind = function(context, ...args) {
  const fn = this;
  return function(...moreArgs) {
    return fn.apply(
      this instanceof fn ? this : context,
      [...args, ...moreArgs]
    );
  };
};

第五部分:浏览器与性能优化(题目36-45)

题目36:内存泄漏的常见原因与解决方案

// 原因1:意外的全局变量
function leaky() {
  leaked = "这是全局变量"; // 没有var/let/const
}

// 原因2:未清理的定时器
class Component {
  constructor() {
    // 问题:组件销毁后,定时器仍在运行
    this.timer = setInterval(() => this.update(), 1000);
  }

  destroy() {
    clearInterval(this.timer); // 必须清理!
  }
}

// 原因3:DOM引用泄漏
const elements = {
  button: document.getElementById("btn")
};
document.body.removeChild(elements.button);
// elements.button仍然持有DOM引用!
elements.button = null; // 需要手动释放

// 原因4:闭包中意外保留大对象
function createLeak() {
  const largeData = new Array(1000000).fill("x");
  return function() {
    // 即使没有使用largeData,它也被闭包引用无法回收
    console.log("hello");
  };
}

题目37:如何优化长列表渲染

// 虚拟列表(Virtual List)核心原理
class VirtualList {
  constructor(container, itemHeight, totalItems, renderItem) {
    this.container = container;
    this.itemHeight = itemHeight;
    this.totalItems = totalItems;
    this.renderItem = renderItem;
    this.scrollTop = 0;

    container.style.height = `${totalItems * itemHeight}px`;
    container.style.position = "relative";

    this.viewport = document.createElement("div");
    container.parentElement.style.overflow = "auto";
    container.parentElement.addEventListener("scroll", this.onScroll.bind(this));

    this.render();
  }

  onScroll(e) {
    this.scrollTop = e.target.scrollTop;
    this.render();
  }

  render() {
    const viewportHeight = this.container.parentElement.clientHeight;
    const startIndex = Math.floor(this.scrollTop / this.itemHeight);
    const endIndex = Math.min(
      startIndex + Math.ceil(viewportHeight / this.itemHeight) + 1,
      this.totalItems
    );

    // 只渲染可见区域的元素
    const fragment = document.createDocumentFragment();
    for (let i = startIndex; i < endIndex; i++) {
      const item = this.renderItem(i);
      item.style.position = "absolute";
      item.style.top = `${i * this.itemHeight}px`;
      fragment.appendChild(item);
    }

    this.container.innerHTML = "";
    this.container.appendChild(fragment);
  }
}

题目38-45:更多浏览器题目

// 题目38:requestAnimationFrame vs setTimeout
// RAF与屏幕刷新率同步(60fps = 每16.67ms)
function animate() {
  // 更新动画状态
  element.style.left = `${position++}px`;
  if (position < 300) {
    requestAnimationFrame(animate); // 下一帧继续
  }
}
requestAnimationFrame(animate);

// 题目39:Service Worker缓存策略
self.addEventListener("fetch", (event) => {
  event.respondWith(
    caches.match(event.request).then((cached) => {
      // Cache First策略:优先使用缓存
      return cached || fetch(event.request).then((response) => {
        const clone = response.clone();
        caches.open("v1").then((cache) => cache.put(event.request, clone));
        return response;
      });
    })
  );
});

// 题目40:CORS跨域解决方案
// 1. 代理服务器(最常用)
// 2. JSONP(只支持GET,已淘汰)
// 3. 服务端配置CORS头
// Access-Control-Allow-Origin: *
// Access-Control-Allow-Methods: GET, POST, PUT, DELETE
// Access-Control-Allow-Headers: Content-Type, Authorization

第六部分:TypeScript与新特性(题目46-50)

题目46:TypeScript高级类型

// 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never;

// 映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// 模板字面量类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"

// 实用示例:深度Readonly
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

题目47:ES2025/2026新特性

// Array.fromAsync(ES2024)
const asyncIterable = {
  async *[Symbol.asyncIterator]() {
    yield 1; yield 2; yield 3;
  }
};
const arr = await Array.fromAsync(asyncIterable); // [1, 2, 3]

// 使用with()方法(不可变更新)
const original = [1, 2, 3, 4, 5];
const modified = original.with(2, 99); // [1, 2, 99, 4, 5]
console.log(original); // [1, 2, 3, 4, 5](原数组不变)

// findLast / findLastIndex
const arr2 = [1, 2, 3, 4, 5];
arr2.findLast(x => x % 2 === 0); // 4
arr2.findLastIndex(x => x % 2 === 0); // 3

// 正则表达式v标志(ES2024)
// 支持Unicode集合操作
const regex = /[\p{Emoji}&&\p{ASCII}]/v;

题目48-50:综合手写题

// 题目48:实现lodash.get
function get(obj, path, defaultValue) {
  const keys = Array.isArray(path) ? path : path.split(/[.[\]]+/).filter(Boolean);
  let result = obj;

  for (const key of keys) {
    result = result?.[key];
    if (result === undefined) return defaultValue;
  }
  return result;
}

get({ a: { b: { c: 42 } } }, "a.b.c");    // 42
get({ a: [1, 2, 3] }, "a[1]");             // 2
get({ a: 1 }, "b.c", "默认值");            // "默认值"

// 题目49:实现图片懒加载
function lazyLoadImages() {
  const images = document.querySelectorAll("img[data-src]");

  const observer = new IntersectionObserver((entries, obs) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.dataset.src;
        img.removeAttribute("data-src");
        obs.unobserve(img);
      }
    });
  });

  images.forEach(img => observer.observe(img));
}

// 题目50:实现简单的状态管理
class Store {
  #state;
  #listeners = new Set();

  constructor(initialState) {
    this.#state = initialState;
  }

  getState() {
    return structuredClone(this.#state);
  }

  setState(updater) {
    const newState = typeof updater === "function"
      ? updater(this.#state)
      : { ...this.#state, ...updater };

    this.#state = newState;
    this.#listeners.forEach(listener => listener(this.#state));
  }

  subscribe(listener) {
    this.#listeners.add(listener);
    return () => this.#listeners.delete(listener); // 返回取消订阅函数
  }
}

面试备考策略

按难度分层准备

基础题(必须全部掌握):题目1-15,考察JS基础概念,任何级别都会考

中级题(Junior到Mid必考):题目16-35,考察实际工程能力和设计模式

高级题(Senior必考):题目36-50,考察深度理解和解决复杂问题的能力

代码书写注意事项

  1. 先说思路,再写代码——展示你的思维过程
  2. 考虑边界情况(null、undefined、空数组等)
  3. 主动提出优化方向(时间复杂度、空间复杂度)
  4. 测试代码是否可运行

在准备过程中,正则表达式测试工具可以帮助你练习和验证正则相关题目,JSON格式化工具有助于理解数据结构相关题目,JWT解码工具则对理解认证相关面试题非常有帮助。

面试前的最后准备

  • 刷题:LeetCode(算法)+ JavaScript专项题
  • 理解:不要死记硬背,理解原理才能应对追问
  • 表达:练习用清晰的语言解释技术概念
  • 项目:准备好用具体项目经历印证你的技术深度

祝面试顺利!

相关文章