JS 速查 | 前端面试基础知识点速览

240 阅读8分钟

要面试了整理了一份前端基础知识点速查手册,涵盖变量、数据类型、作用域、闭包、原型链、异步编程及 ES6 特性,直击要点,简单易懂。

内容经过ai二次编辑补全优化,看着没啥问题,有问题欢迎指出。


1. letvar 和 const 的区别

  • var

    • 变量提升:声明被提升到作用域顶部,值为 undefined
    • 函数作用域:在函数内声明则为局部变量,否则为全局变量。
    • 允许重复声明:可能导致变量覆盖。
  • let

    • 块级作用域:仅在 {} 内有效(如 iffor)。
    • 暂时性死区(TDZ):声明前访问会抛出 ReferenceError
    • 禁止重复声明:同一作用域内不可重复定义。
  • const

    • 块级作用域,同 let
    • 声明时必须初始化,且不可重新赋值。
    • 引用类型(如对象、数组)内部属性可修改,但不可指向新对象。

2. 数据类型

  • 基本类型(7种)

    • StringNumberBooleanUndefinedNullBigInt(大整数)、Symbol(唯一值)。
    • 按值存储,直接比较值。
  • 引用类型

    • ObjectArrayFunctionDateSetMap 等。
    • 按引用存储,比较时比较内存地址。

3. 堆和栈

  • 栈内存

    • 存储基本类型值和引用类型的地址指针。
    • 由系统自动分配和释放,效率高但空间有限。
  • 堆内存

    • 存储引用类型的实际数据。
    • 动态分配空间,需手动管理(JS通过垃圾回收自动处理)。

4. 深拷贝与浅拷贝

  • 浅拷贝

    • 实现方式:

      • Object.assign({}, obj)
      • 展开运算符 {...obj} / [...arr]
      • Array.prototype.slice() / concat()
    • 注意:嵌套对象仍共享引用。

  • 深拷贝

    • JSON方法

      • JSON.parse(JSON.stringify(obj))
      • 缺陷:丢失函数、undefinedSymbol;无法处理循环引用;日期转为字符串。
    • 递归实现

      • 需处理循环引用(使用 WeakMap 缓存)、特殊类型(如 DateRegExp)。
    • 现代 API

      • structuredClone()(浏览器环境,支持部分类型)。
    • 库函数:Lodash _.cloneDeep()


5. this 的指向

  • this对象不同环境绑定的对象不同。
  • 有显示绑定,隐式绑定,默认绑定
  • 可以通过callapplybind修改

6. 普通函数 vs 箭头函数

  • 普通函数

    • this 动态绑定
      • 默认绑定:非严格模式指向 window,严格模式为 undefined
      • 隐式绑定:由调用对象决定(如 obj.fn()this 指向 obj)。
      • 显式绑定:callapplybind 指定 this
    • 可通过 new Function() 创建函数。
    • arguments
  • 箭头函数

    • 无自身 this,继承外层作用域的 this
    • 不可用作构造函数。
    • 无 arguments 对象。

7. callapplybind

  • 共同点:修改函数 this 指向。

  • 区别

    • call:立即执行,参数逐个传递(fn.call(obj, 1, 2))。
    • apply:立即执行,参数以数组传递(fn.apply(obj, [1, 2]))。
    • bind:返回新函数,可预设参数(const newFn = fn.bind(obj, 1))。

8. 数据类型判断

  • typeof

    • 返回字符串,如 typeof 'str' → "string"
    • 局限性:typeof null → "object",无法区分对象类型。
  • instanceof

    • 检测构造函数的 prototype 是否在对象原型链上。
    • 示例:arr instanceof Array → true
  • Object.prototype.toString

    • 最精确方式:Object.prototype.toString.call(obj).slice(8, -1)
    • 结果如 "[object Array]""[object Date]"

9. instanceof 原理

  • 递归检查对象的 __proto__ 是否等于构造函数的 prototype

  • 手写实现:

    javascript

    复制

    function myInstanceof(obj, Constructor) {
      let proto = Object.getPrototypeOf(obj);
      while (proto) {
        if (proto === Constructor.prototype) return true;
        proto = Object.getPrototypeOf(proto);
      }
      return false;
    }
    

10. 原型与原型链

  • 构造函数:通过 new 创建对象,其 prototype 属性为实例原型。

  • 实例:通过 __proto__ 访问原型,形成链式结构。

  • 原型链终点Object.prototype.__proto__ → null

  • 示例

    javascript

    复制

    function Person() {}
    const p = new Person();
    // p.__proto__ === Person.prototype
    // Person.prototype.__proto__ === Object.prototype
    

11. 继承

  • ES5 常用继承方式

    1. 原型链继承:子类原型指向父类实例。

      • 缺点:父类引用属性被所有子类实例共享。
    2. 构造函数继承:在子类构造函数中调用父类构造函数。

      • 缺点:无法继承父类原型方法。
    3. 组合继承:结合原型链和构造函数。

      • 缺点:父类构造函数被调用两次。
    4. 寄生组合继承:优化组合继承,通过 Object.create() 避免重复调用父类构造函数。

  • ES6 class 继承

    • 使用 extends 和 super,本质是语法糖,底层基于原型链。

12. new 操作符过程

  1. 创建空对象 obj,设置 obj.__proto__ = Constructor.prototype
  2. 执行构造函数,Constructor.call(obj, ...args)
  3. 若构造函数返回对象,则返回该对象;否则返回 obj

13. ES6+ 核心特性

  • 语法增强

    • 箭头函数、模板字符串、解构赋值。
    • 默认参数、剩余参数 ...、展开运算符。
    • class 类与继承、super 关键字。
  • 数据结构

    • Set(去重集合)、Map(键值对)、WeakMap(弱引用)。
  • 异步编程

    • Promiseasync/await
  • 模块化

    • import / export,支持静态分析和 Tree Shaking。

14. Promise

  • 状态

    • pending → fulfilledresolve)或 rejectedreject)。
  • 链式调用

    javascript

    复制

    fetch(url)
      .then(res => res.json())
      .catch(err => console.log(err))
      .finally(() => { /* 清理逻辑 */ });
  • 静态方法

    • Promise.all():全部成功则返回结果数组,任一失败则立即拒绝。
    • Promise.race():首个完成的 Promise 决定结果。
    • Promise.allSettled():等待所有 Promise 完成,返回状态描述数组。

15. 作用域与作用域链

  • 共享属性的载体
    构造函数的 prototype 对象用于存放所有实例共享的属性和方法。
    实例对象的 __proto__ 指向其构造函数的 prototype

  • 原型链
    当访问对象属性时,若该属性在对象本身不存在,会沿着 __proto__ 一层层查找,直到找到为止。
    最顶层为 Object.prototype,其 __proto__null,查找到此处终止,这就构成了原型链。

  • 优点

    • 共享属性和方法,避免每个实例都持有一份,节省内存。
    • 通过原型链实现继承,使得对象能访问到父级原型上定义的成员。
  • 缺点

    • 如果原型上存放了可变数据(例如数组、对象),一处的修改可能会影响所有实例,因为它们共享同一引用。
  • 继承
    原型机制是实现继承的重要手段,通过设置原型链,子对象可以继承父对象的属性和方法。

obj.__proto__.constructor.prototype.__proto__

16. 闭包

  • 定义:函数和它当时能用到的变量“绑”在一起。即使这个函数被放到别的地方执行,它依然能记住并使用那些原本属于它的变量。表现就是函数访问外部的变量。

  • 应用场景

    • 模块化(私有变量)。
    • 防抖/节流、柯里化。
  • 内存泄漏

    • 示例:未清理的 DOM 事件监听器引用闭包变量。

例子 1:计数器

// 在这个例子中,`createCounter` 函数返回一个内部函数,这个内部函数可以访问并修改外部函数中的变量 `count`。即使 `createCounter` 执行完毕后,返回的函数依然能记住 `count` 的值。
function createCounter() {
  let count = 0; // 外部变量

  // 内部函数,形成闭包,可以访问 count
  return function () {
    count++;
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 输出: 1
counter(); // 输出: 2

例子 2:带参数的闭包

// 这个例子展示了如何通过闭包“记住”外部函数传入的参数,从而生成一个带有特定前缀的日志函数。
function createLogger(prefix) {
  // 内部函数可以访问外部参数 prefix
  return function(message) {
    console.log(`${prefix}: ${message}`);
  };
}

const infoLogger = createLogger("INFO");
const errorLogger = createLogger("ERROR");

infoLogger("服务器启动成功"); // 输出: INFO: 服务器启动成功
errorLogger("连接失败");        // 输出: ERROR: 连接失败

例子 3:延迟执行

// 这个例子展示了闭包在异步场景下的应用。内部函数 `delayedFunction` 延迟执行时依然能访问到外部变量 `msg`。
function delayedGreeting() {
  const msg = "Hello, Closure!";

  // 定时器内的回调形成闭包,能访问 msg
  setTimeout(function delayedFunction() {
    console.log(msg);
  }, 1000);
}

delayedGreeting(); // 1秒后输出: Hello, Closure!

17. 防抖与节流

  • 防抖(Debounce) :多次触发事件,只执行第一次或最后一次事件

    function debounce(fn, delay) {
      let timer;
      return function(...args) {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, args), delay);
      };
    }
    
  • 节流(Throttle) :多次触发事件,只执行固定间隔时间的事件

    function throttle(fn, interval) {
      let lastTime = 0;
      return function(...args) {
        const now = Date.now();
        if (now - lastTime >= interval) {
          fn.apply(this, args);
          lastTime = now;
        }
      };
    }
    

18. 事件循环(Event Loop)

  • 执行顺序

    1. 同步代码(调用栈)。
    2. 所有微任务(Promise.thenMutationObserver)。
    3. 一个宏任务(如 setTimeout)。
    4. 重复步骤 2-3。
    console.log(1);
    setTimeout(() => console.log(2), 0);
    Promise.resolve().then(() => console.log(3));
    console.log(4);
    // 输出顺序:1 → 4 → 3 → 2

19. 模块化

  • CommonJS

    • 同步加载,适用于 Node.js。
    • module.exports 导出,require() 导入。
  • ES Module

    • 静态分析,支持 Tree Shaking。
    • export / import 语法,浏览器原生支持。
  • 差异

    • CommonJS 输出值的拷贝,ES Module 输出值的引用。
    • ES Module 顶层 this 为 undefined,CommonJS 为 exports 对象。

20. DOM 事件传播

  • 事件委托

    • 利用冒泡,将事件监听器绑定到父元素,通过 event.target 处理子元素事件。
    • 优点:减少内存消耗,动态元素无需重新绑定。
  • 阻止默认行为

    • event.preventDefault()(如阻止表单提交)。

21. 垃圾回收

  • 标记清除:从根对象(全局变量)出发,标记可达对象,清除未标记的。

  • 引用计数:记录对象被引用次数,归零时回收。缺陷:循环引用无法回收。

  • 内存泄漏场景

    • 意外全局变量。
    • 未解绑的事件监听器、定时器。
    • 闭包中未释放的变量。