Skip to content

JavaScript 基础与进阶

JavaScript 是前端开发的核心语言。本章节包含基础概念、进阶特性以及大量实战面试题。

专题模块


面试题精选

1. 闭包(Closure)

  • 题目:请解释什么是闭包?并写一个计数器函数示例。
  • 考点:作用域链、函数执行上下文、内存管理。

参考答案

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

javascript
function counter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

const inc = counter();
console.log(inc()); // 1
console.log(inc()); // 2

2. 原型链与继承

  • 题目:JavaScript 中如何实现继承?原型链有什么作用?
  • 考点:原型对象、__proto__Object.create、ES6 class。

参考答案

JS 中对象通过 [[prototype]](即 __proto__)属性连接形成原型链,实现继承和属性共享。所有对象最终继承自 Object.prototype。终点是 null

3. this 指向

  • 题目:解释 this 在不同场景下的指向,包括普通函数、箭头函数、事件处理、call/apply/bind。
  • 考点:执行上下文、绑定规则。

参考答案

this 指向取决于函数调用方式。

  • 普通函数默认指向全局对象(严格模式下为 undefined);
  • 方法调用指向调用该方法的对象;
  • 构造函数指向新创建的实例;
  • 箭头函数没有自己的 this,继承自外层作用域;
  • call/apply/bind 可以显式设置 this

优先级

  1. new 绑定
  2. 显式绑定(call/apply/bind
  3. 隐式绑定(对象方法调用)
  4. 默认绑定(普通函数调用,严格模式下为 undefined
  5. 箭头函数继承外层 this

4. 事件循环与异步机制

  • 题目:解释 JS 的事件循环(Event Loop)和微任务(Microtask)与宏任务(Macrotask)的区别。
  • 考点:Promise、setTimeout、MutationObserver。

参考答案

JavaScript 是单线程的,事件循环是 JS 处理异步操作的机制。

  • 宏任务:包括 setTimeoutsetInterval、I/O 操作等。
  • 微任务:包括 Promise.thenMutationObserver 等。

每次事件循环会先执行所有微任务队列,然后再执行一个宏任务。

执行顺序

  1. 执行全局代码(同步任务)
  2. 执行微任务队列 (Promise.then / MutationObserver)
  3. 执行一个宏任务 (setTimeout / I/O 操作)
  4. 重复步骤 2 和 3。

口诀:同步 → 微任务 → 宏任务

5. 深浅拷贝

  • 题目:写出深拷贝和浅拷贝的实现方式,并说明各自区别。
  • 考点:对象引用、循环引用、JSON 方法、递归实现。

参考答案

区别:浅拷贝只复制对象的第一层属性,深拷贝会递归复制所有层级的属性。

javascript
// --- 深拷贝 ---

// 方式1: JSON (不支持函数、undefined、Symbol等)
const deepCloneJSON = JSON.parse(JSON.stringify(target));

// 方式2: structuredClone (现代浏览器支持)
const deepCloneStructured = structuredClone(target);

// 方式3: 递归实现
function deepClone(target) {
  if (typeof target !== "object" || target === null) {
    return target;
  }

  let cloneTarget = Array.isArray(target) ? [] : {};
  for (let key in target) {
    if (target.hasOwnProperty(key)) {
      cloneTarget[key] = deepClone(target[key]);
    }
  }
  return cloneTarget;
}

// --- 浅拷贝 ---

// 方式1: Object.assign
const shallowClone1 = Object.assign({}, target);

// 方式2: 扩展运算符
const shallowClone2 = { ...target };

// 方式3: 手动实现
function shallowClone3(target) {
  if (typeof target !== "object" || target === null) {
    return target;
  }
  let cloneTarget = Array.isArray(target) ? [] : {};
  for (let key in target) {
    if (target.hasOwnProperty(key)) {
      cloneTarget[key] = target[key];
    }
  }
  return cloneTarget;
}

6. 数组去重、排序、求和

  • 题目:实现数组去重、数组求和、数组中两个数和等于目标值的算法。
  • 考点:Set、Map、双指针、reduce。

参考答案

javascript
// 1. 数组去重
function uniqueArray(arr) {
  return [...new Set(arr)];
}

// 2. 数组求和
const sumArray = (arr) => arr.reduce((total, val) => total + val, 0);

// 3. 数组中两数和等于目标值 (Two Sum)
function twoSum(arr, target) {
  const mp = new Map();
  for (let i = 0; i < arr.length; i++) {
    const diff = target - arr[i];
    if (mp.has(diff)) {
      return [mp.get(diff), i];
    }
    mp.set(arr[i], i);
  }
  return [];
}

7. 模块化

  • 题目:ESM 和 CommonJS 的区别,为什么 Tree Shaking 只支持 ESM。
  • 考点:静态分析、模块加载机制。

参考答案

  1. ESM (ECMAScript Modules)

    • JavaScript 的原生模块系统。
    • 静态分析:允许在编译时确定依赖关系,从而实现 Tree Shaking(去除未使用代码)。
    • 语法:使用 import / export
    • 加载方式:静态加载。
    • 执行时机:编译时解析。
    • this 指向:顶层的 thisundefined
    • 适用场景:前端和现代 JavaScript 项目。
  2. CommonJS

    • Node.js 采用的模块系统。
    • 动态加载:无法在编译时确定依赖关系,因此 不支持 Tree Shaking。
    • 语法:使用 require / module.exports
    • 加载方式:动态加载。
    • 执行时机:运行时解析。
    • this 指向:顶层的 this 指向 module.exports
    • 适用场景:主要用于 Node.js 环境。

MIT Licensed | Keep Learning.