在寻找前端实习的过程中我们会发现面试除了考察算法题之外手写题同样也是高频考点。尤其是在准备中大厂前端面试时手写能力几乎是必不可少的一部分。这篇文章将围绕几道经典高频手写题展开包括手写深拷贝、实现寄生组合式继承以及数组扁平化帮助大家从原理到底层实现进行系统理解。1.手写深拷贝深拷贝是指创建一个与原对象完全独立的新对象不仅复制对象第一层的属性还会递归复制内部嵌套的对象、数组等引用类型。这样修改新对象时不会影响原对象。它的核心意义在于“彻底断开引用关系”常用于状态管理、数据备份以及避免对象共享带来的副作用等场景。手写深拷贝的核心是递归复制对象中的每一层数据而不是只复制引用地址。实现时需要先判断数据类型基础类型直接返回引用类型则继续递归创建新的对象或数组并逐层复制属性。同时为了处理循环引用问题通常会配合WeakMap记录已经拷贝过的对象避免无限递归。本质上手写深拷贝就是“递归复制结构 断开引用关系”。function deepClone(obj, map new WeakMap()) { // 1. 基本类型直接返回 if (obj null || typeof obj ! object) { return obj } // 2. 处理循环引用 if (map.has(obj)) { return map.get(obj) } // 3. 创建新对象/数组 const newObj Array.isArray(obj) ? [] : {} // 4. 缓存当前对象 map.set(obj, newObj) // 5. 递归拷贝 for (const key in obj) { // 过滤原型链属性 if (obj.hasOwnProperty(key)) { newObj[key] deepClone(obj[key], map) } } return newObj } /* 这段 deepClone 首先判断如果是基本类型或者 null就直接返回 如果是对象就先通过 WeakMap 判断是否已经拷贝过用来解决循环引用问题。 然后根据原数据是数组还是对象创建对应的新容器并立刻把原对象和新对象的映射关系存到 WeakMap 中。 最后遍历对象自身属性对每个属性递归调用 deepClone实现真正的深层复制。这样拷贝出来的新对象和原对象在嵌套引用上也是相互独立的。 deepClone 中使用 WeakMap 而不是 Map主要是为了避免内存泄漏。 因为 Map 对 key 是强引用即使外部对象已经不再使用只要 Map 中还保存着这个对象作为 key垃圾回收器就无法释放它。 而 WeakMap 对 key 是弱引用不会阻止垃圾回收更适合用来做 deepClone 这种临时缓存的循环引用处理。并且 WeakMap 的 key 只能是对象也正符合 deepClone 的使用场景。 */2.手写寄生组合式继承寄生组合式继承是 JavaScript 中一种比较经典且高效的继承方案它结合了“原型链继承”和“借用构造函数继承”的优点。实现时子类通过Parent.call(this)继承父类实例属性避免属性共享问题同时通过Object.create(Parent.prototype)继承父类原型方法从而避免多次调用父类构造函数带来的性能浪费。本质上它实现了“实例属性独立 原型方法复用”也是 ES6class extends出现前最推荐的继承方式之一。手写寄生组合式继承的核心是组合“构造函数继承”和“原型继承”两种方式先通过Parent.call(this)让子类继承父类的实例属性和方法再通过Object.create(Parent.prototype)创建一个以父类原型为基础的新对象赋值给子类原型从而实现原型方法复用并修正constructor指向。这样既避免了引用属性共享的问题又减少了父类构造函数的重复调用是 ES5 中比较完善的一种继承实现方案。function Parent(name) { this.name name this.colors [red, blue] } Parent.prototype.sayName function () { console.log(this.name) } function Child(name, age) { // 1. 继承父类实例属性 Parent.call(this, name) this.age age } // 2. 继承父类原型方法 Child.prototype Object.create(Parent.prototype) // 3. 修正 constructor 指向 Child.prototype.constructor Child Child.prototype.sayAge function () { console.log(this.age) }3.手写数组扁平化数组扁平化是指将多层嵌套的数组转换成一个一维数组的过程例如把[1, [2, [3, 4]]]转换为[1, 2, 3, 4]。它的核心思想是“递归展开嵌套结构”在前端开发中常用于数据处理、树形结构转换以及接口数据整理等场景。实现方式通常包括递归、reduce、扩展运算符以及 ES6 的flat()方法等。手写数组扁平化的核心是递归遍历数组中的每一项如果当前元素是数组就继续递归展开如果是普通元素就直接放入结果数组中最终将多层嵌套结构转换成一维数组。实现时常见的方法有递归、reduce、栈结构以及扩展运算符等本质上就是“遍历嵌套结构并不断展开”。function flatten(arr, depth 1) { if (depth 0) { return arr.slice() } const res [] for (const item of arr) { if (Array.isArray(item)) { res.push(...flatten(item, depth - 1)) } else { res.push(item) } } return res }
前端实习面试手写题分享
在寻找前端实习的过程中我们会发现面试除了考察算法题之外手写题同样也是高频考点。尤其是在准备中大厂前端面试时手写能力几乎是必不可少的一部分。这篇文章将围绕几道经典高频手写题展开包括手写深拷贝、实现寄生组合式继承以及数组扁平化帮助大家从原理到底层实现进行系统理解。1.手写深拷贝深拷贝是指创建一个与原对象完全独立的新对象不仅复制对象第一层的属性还会递归复制内部嵌套的对象、数组等引用类型。这样修改新对象时不会影响原对象。它的核心意义在于“彻底断开引用关系”常用于状态管理、数据备份以及避免对象共享带来的副作用等场景。手写深拷贝的核心是递归复制对象中的每一层数据而不是只复制引用地址。实现时需要先判断数据类型基础类型直接返回引用类型则继续递归创建新的对象或数组并逐层复制属性。同时为了处理循环引用问题通常会配合WeakMap记录已经拷贝过的对象避免无限递归。本质上手写深拷贝就是“递归复制结构 断开引用关系”。function deepClone(obj, map new WeakMap()) { // 1. 基本类型直接返回 if (obj null || typeof obj ! object) { return obj } // 2. 处理循环引用 if (map.has(obj)) { return map.get(obj) } // 3. 创建新对象/数组 const newObj Array.isArray(obj) ? [] : {} // 4. 缓存当前对象 map.set(obj, newObj) // 5. 递归拷贝 for (const key in obj) { // 过滤原型链属性 if (obj.hasOwnProperty(key)) { newObj[key] deepClone(obj[key], map) } } return newObj } /* 这段 deepClone 首先判断如果是基本类型或者 null就直接返回 如果是对象就先通过 WeakMap 判断是否已经拷贝过用来解决循环引用问题。 然后根据原数据是数组还是对象创建对应的新容器并立刻把原对象和新对象的映射关系存到 WeakMap 中。 最后遍历对象自身属性对每个属性递归调用 deepClone实现真正的深层复制。这样拷贝出来的新对象和原对象在嵌套引用上也是相互独立的。 deepClone 中使用 WeakMap 而不是 Map主要是为了避免内存泄漏。 因为 Map 对 key 是强引用即使外部对象已经不再使用只要 Map 中还保存着这个对象作为 key垃圾回收器就无法释放它。 而 WeakMap 对 key 是弱引用不会阻止垃圾回收更适合用来做 deepClone 这种临时缓存的循环引用处理。并且 WeakMap 的 key 只能是对象也正符合 deepClone 的使用场景。 */2.手写寄生组合式继承寄生组合式继承是 JavaScript 中一种比较经典且高效的继承方案它结合了“原型链继承”和“借用构造函数继承”的优点。实现时子类通过Parent.call(this)继承父类实例属性避免属性共享问题同时通过Object.create(Parent.prototype)继承父类原型方法从而避免多次调用父类构造函数带来的性能浪费。本质上它实现了“实例属性独立 原型方法复用”也是 ES6class extends出现前最推荐的继承方式之一。手写寄生组合式继承的核心是组合“构造函数继承”和“原型继承”两种方式先通过Parent.call(this)让子类继承父类的实例属性和方法再通过Object.create(Parent.prototype)创建一个以父类原型为基础的新对象赋值给子类原型从而实现原型方法复用并修正constructor指向。这样既避免了引用属性共享的问题又减少了父类构造函数的重复调用是 ES5 中比较完善的一种继承实现方案。function Parent(name) { this.name name this.colors [red, blue] } Parent.prototype.sayName function () { console.log(this.name) } function Child(name, age) { // 1. 继承父类实例属性 Parent.call(this, name) this.age age } // 2. 继承父类原型方法 Child.prototype Object.create(Parent.prototype) // 3. 修正 constructor 指向 Child.prototype.constructor Child Child.prototype.sayAge function () { console.log(this.age) }3.手写数组扁平化数组扁平化是指将多层嵌套的数组转换成一个一维数组的过程例如把[1, [2, [3, 4]]]转换为[1, 2, 3, 4]。它的核心思想是“递归展开嵌套结构”在前端开发中常用于数据处理、树形结构转换以及接口数据整理等场景。实现方式通常包括递归、reduce、扩展运算符以及 ES6 的flat()方法等。手写数组扁平化的核心是递归遍历数组中的每一项如果当前元素是数组就继续递归展开如果是普通元素就直接放入结果数组中最终将多层嵌套结构转换成一维数组。实现时常见的方法有递归、reduce、栈结构以及扩展运算符等本质上就是“遍历嵌套结构并不断展开”。function flatten(arr, depth 1) { if (depth 0) { return arr.slice() } const res [] for (const item of arr) { if (Array.isArray(item)) { res.push(...flatten(item, depth - 1)) } else { res.push(item) } } return res }