返回

前端面试100道手写题(4)—— 深浅拷贝

前端

前言

深浅拷贝是前端面试中经常被问到的经典题目。它不仅仅考察的是实现逻辑,更是对整个Javascript语言背后的理论知识的考察,包括基础数据类型、原型链等等。下面,我们就从简单到困难,一步步实现深浅拷贝。

1. 基础数据类型的深浅拷贝

对于基础数据类型(如数字、字符串、布尔值),深浅拷贝和浅拷贝是没有区别的。因为基础数据类型在内存中存储的是实际值,而不是引用。因此,无论使用哪种拷贝方式,都会得到一个新的值。

// 基础数据类型的深拷贝
const num1 = 1;
const num2 = num1;
num2 += 1;
console.log(num1); // 1

// 基础数据类型的浅拷贝
const str1 = 'hello';
const str2 = str1;
str2 += 'world';
console.log(str1); // hello

2. 引用数据类型的浅拷贝

对于引用数据类型(如对象、数组),浅拷贝只拷贝引用,不会拷贝实际值。因此,如果修改浅拷贝后的对象或数组,原始对象或数组也会受到影响。

// 引用数据类型的浅拷贝
const obj1 = { name: '张三' };
const obj2 = obj1;
obj2.name = '李四';
console.log(obj1); // { name: '李四' }

3. 引用数据类型的深拷贝

为了实现引用数据类型的深拷贝,需要遍历对象或数组的每一个属性,并为每个属性创建一个新的副本。

// 引用数据类型的深拷贝
function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  if (Array.isArray(obj)) {
    const newArr = [];
    for (const item of obj) {
      newArr.push(deepCopy(item));
    }
    return newArr;
  }

  const newObj = {};
  for (const key in obj) {
    newObj[key] = deepCopy(obj[key]);
  }
  return newObj;
}

const obj1 = { name: '张三', age: 20 };
const obj2 = deepCopy(obj1);
obj2.name = '李四';
console.log(obj1); // { name: '张三', age: 20 }

4. 使用JSON实现深拷贝

JSON.parse()和JSON.stringify()可以实现引用数据类型的深拷贝。JSON.stringify()将对象或数组转换成JSON字符串,JSON.parse()将JSON字符串转换成对象或数组。

// 使用JSON实现深拷贝
const obj1 = { name: '张三', age: 20 };
const obj2 = JSON.parse(JSON.stringify(obj1));
obj2.name = '李四';
console.log(obj1); // { name: '张三', age: 20 }

5. 考虑循环引用的深拷贝

在某些情况下,对象或数组中可能存在循环引用。如果直接使用深拷贝算法,可能会陷入死循环。为了解决这个问题,可以在深拷贝算法中使用一个Map来记录已经拷贝过的对象或数组,如果遇到循环引用,则直接返回Map中对应的对象或数组。

// 考虑循环引用的深拷贝
function deepCopyWithCycle(obj, map = new Map()) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  if (map.has(obj)) {
    return map.get(obj);
  }

  if (Array.isArray(obj)) {
    const newArr = [];
    for (const item of obj) {
      newArr.push(deepCopyWithCycle(item, map));
    }
    map.set(obj, newArr);
    return newArr;
  }

  const newObj = {};
  for (const key in obj) {
    newObj[key] = deepCopyWithCycle(obj[key], map);
  }
  map.set(obj, newObj);
  return newObj;
}

const obj1 = { name: '张三', age: 20 };
obj1.friend = obj1;
const obj2 = deepCopyWithCycle(obj1);
obj2.name = '李四';
console.log(obj1); // { name: '张三', age: 20, friend: { name: '李四', age: 20, friend: ... } }

总结

深浅拷贝是前端面试中常见的题目,也是对Javascript语言背后的理论知识的考察。通过对基础数据类型和引用数据类型的深浅拷贝实现的讲解,我们可以深入理解Javascript语言的底层机制和原型链的概念。在实际开发中,根据不同的场景选择合适的拷贝方式,可以避免很多潜在的bug。