前端面试必备 | 深拷贝和浅拷贝篇(P1-12)

alt

文章目录

  1. 什么是深拷贝和浅拷贝?它们之间有什么区别?
  2. 如何进行浅拷贝?请列举一些浅拷贝的方法或技巧。
  3. 请说明一下浅拷贝的应用场景,并举例说明。
  4. 如何进行深拷贝?请列举一些深拷贝的方法或技巧。
  5. 请说明一下深拷贝的应用场景,并举例说明。
  6. 如何使用JSON.stringify和JSON.parse实现深拷贝?
  7. 请解释一下Object.assign方法的拷贝特性,并说明它的局限性。
  8. 请说明一下使用扩展运算符(...)进行对象和数组的浅拷贝的实现原理。
  9. 请解释一下循环引用对深拷贝的影响,并提供一种解决循环引用的方法。
  10. 请解释一下拷贝函数时对函数属性的处理方式,并说明如何拷贝函数的正确姿势。
  11. 介绍一下Lodash中的深拷贝函数_.cloneDeep的实现原理。
  12. 请说明在使用深拷贝时需要注意的一些性能和内存方面的问题。

1. 什么是深拷贝和浅拷贝?它们之间有什么区别?

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)都是在编程中处理对象或数据的复制操作。

浅拷贝创建一个新对象或数据结构,其中包含原始对象的引用。换句话说,新对象与原始对象共享相同的内存地址,因此对其中一个对象进行更改会影响到另一个对象。浅拷贝仅复制对象的第一层结构,而不会递归复制嵌套的对象或数据

深拷贝创建一个全新的对象或数据结构,其中包含原始对象完全独立的副本。新对象与原始对象具有不同的内存地址,因此彼此之间的更改是相互独立的。深拷贝会递归复制所有嵌套的对象或数据,确保整个对象及其子对象都被复制

区别:

  1. 内存引用:浅拷贝复制的是对象的引用,深拷贝复制的是对象的值。

  2. 对象的变化:对浅拷贝的修改会影响原始对象,因为它们共享相同的引用。而对深拷贝的修改不会影响原始对象。

  3. 嵌套对象或数据的复制:浅拷贝仅复制第一层对象或数据,嵌套的对象或数据仍然是共享的。深拷贝通过递归复制嵌套的对象或数据,确保每个对象都有独立的副本。

在实际编程中,选择使用深拷贝还是浅拷贝取决于具体的需求。如果需要对对象进行修改而不影响原始对象,或者处理嵌套的对象结构,那么深拷贝是更合适的选择。而对于简单的数据结构或者只需要引用原始对象的情况,浅拷贝可能更加高效和节省内存。

2. 如何进行浅拷贝?请列举一些浅拷贝的方法或技巧。

进行浅拷贝有多种方法和技巧,以下是一些常见的浅拷贝方法:

1. 扩展运算符(Spread Operator):使用扩展运算符可以创建一个对象或数组的浅拷贝副本。

const originalObj = { name: "John", age: 30 };
const shallowCopyObj = { ...originalObj };

2. Object.assign() 方法:使用 Object.assign() 可以将一个或多个源对象的属性复制到目标对象,并返回目标对象的浅拷贝。

const originalObj = { name: "John", age: 30 };
const shallowCopyObj = Object.assign({}, originalObj);

3. Array.slice() 方法:对于数组,可以使用 Array.slice() 方法来创建一个浅拷贝的副本。

const originalArr = [1, 2, 3, 4, 5];
const shallowCopyArr = originalArr.slice();

4. Array.concat() 方法:使用 Array.concat() 方法也可以在数组中创建一个浅拷贝。

const originalArr = [1, 2, 3, 4, 5];
const shallowCopyArr = originalArr.concat();

alt

需要注意的是,这些方法只复制了对象或数组的第一层结构,如果存在嵌套对象或数组,则仍然是浅拷贝,即嵌套的对象或数组将被共享。如果需要进行深层复制,需要使用深拷贝方法,如 JSON.parse(JSON.stringify()) 或第三方库(如 lodashcloneDeep() 方法)。

3. 请说明一下浅拷贝的应用场景,并举例说明。

浅拷贝在以下情况下可以派上用场:

1. 复制简单的数据结构:对于简单的数据结构,如基本数据类型(字符串、数字等)或对象和数组,浅拷贝是足够的。可以使用浅拷贝来创建这些数据的副本,以便在不影响原始数据的情况下进行操作。

const originalNum = 42;
const shallowCopyNum = originalNum;
shallowCopyNum = 10;
console.log(originalNum); // 输出: 42
console.log(shallowCopyNum); // 输出: 10

2. 传递引用而不是副本:有时,需要将对象或数组传递给函数或作为参数传递给其他对象,但不希望修改原始对象。在这种情况下,使用浅拷贝可以传递引用,而不是复制整个对象。

const originalArray = [1, 2, 3];
function processArray(arr) {
  // 对传入的数组进行操作,但不修改原始数组
  arr.push(4);
  console.log(arr); // 输出: [1, 2, 3, 4]
}
processArray(originalArray);
console.log(originalArray); // 输出: [1, 2, 3, 4]

3. 缓存数据:使用浅拷贝可以在不重新获取数据的情况下创建数据的副本,并在需要时使用副本。这对于避免频繁的网络请求或计算重量较大的数据很有用。

let cachedData = null;
function fetchData() {
  // 如果缓存数据存在,则使用缓存数据
  if (cachedData) {
    return cachedData;
  }
  // 否则进行网络请求或计算
  cachedData = /* 网络请求或计算操作 */;
  return cachedData;
}

alt

需要注意的是,尽管浅拷贝适用于上述场景,但对于嵌套对象或数组,修改副本仍会反映在原始对象上。如果需要避免这种情况,应使用深拷贝。

4. 如何进行深拷贝?请列举一些深拷贝的方法或技巧。

进行深拷贝有多种方法和技巧,以下是一些常见的深拷贝方法:

1. JSON.parse(JSON.stringify()):使用 JSON.stringify() 将对象转换为 JSON 字符串,再使用 JSON.parse() 将字符串解析回对象。这种方法能够实现一层深拷贝,适用于没有循环引用的简单对象和数组。但是,它无法处理包含函数、RegExp、Date 等特殊类型的对象。

const originalObj = { name: 'John', age: 30 };
const deepCopyObj = JSON.parse(JSON.stringify(originalObj));

2. 第三方库(如 lodash 的 cloneDeep()):许多 JavaScript 的第三方库提供了深拷贝的方法,其中最常见的是 lodash 库的 cloneDeep() 方法。该方法能够处理循环引用和特殊类型的对象。

const originalObj = { name: 'John', age: 30 };
const deepCopyObj = _.cloneDeep(originalObj);

alt

需要注意的是,深拷贝可能会带来性能上的损耗,特别是对于大型和复杂的对象。因此,在进行深拷贝时,应根据实际需求和性能考虑来选择合适的方法。

如果需要进行自定义的深拷贝操作,可以编写递归函数来遍历对象或数组的属性,并对每个属性进行深层复制。这需要考虑到复杂的情况,如嵌套对象、循环引用等,并进行适当的处理。

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  let result;
  if (Array.isArray(obj)) {
    result = [];
    for (let i = 0; i < obj.length; i++) {
      result[i] = deepCopy(obj[i]);
    }
  } else {
    result = {};
    for (let key in obj) {
      if (obj.hasOwnProperty(key)) {
        result[key] = deepCopy(obj[key]);
      }
    }
  }

  return result;
}

const originalObj = { name: 'John', age: 30 };
const deepCopyObj = deepCopy(originalObj);

这是一个简单的深拷贝函数示例,只涵盖了对象和数组的情况。对于更复杂的情况,可能需要进行更多的检查和额外的处理。

5. 请说明一下深拷贝的应用场景,并举例说明。

深拷贝在以下情况下可以派上用场:

1. 保留原始数据的完整性:当需要保留原始数据的完整性并创建一个全新的对象或数组时,深拷贝是必需的。这样可以确保修改副本不会影响原始数据。

const originalObj = { name: 'John', age: 30 };
const deepCopyObj = deepCopy(originalObj);
deepCopyObj.name = 'Jane';
deepCopyObj.age = 25;
console.log(originalObj); // 输出: { name: 'John', age: 30 }
console.log(deepCopyObj); // 输出: { name: 'Jane', age: 25 }

2. 处理含有循环引用的对象:在某些情况下,对象之间可能存在循环引用,即对象A引用了对象B,而对象B又引用了对象A。在这种情况下,使用深拷贝可以处理循环引用并创建完整的副本。

const originalObj = { value: 42 };
originalObj.circularRef = originalObj; // 循环引用
const deepCopyObj = deepCopy(originalObj);
console.log(deepCopyObj.circularRef === deepCopyObj); // 输出: true

3. 避免共享引用导致的意外修改:当多个变量引用同一个对象,并对其中一个变量进行修改时,如果不使用深拷贝,所有引用该对象的变量都会受到影响。深拷贝可以创建独立的副本,避免共享引用导致的意外修改。

const originalArray = [1, 2, 3];
const shallowCopyArray = originalArray;
const deepCopyArray = deepCopy(originalArray);

shallowCopyArray.push(4);
deepCopyArray.push(4);

console.log(originalArray); // 输出: [1, 2, 3, 4]
console.log(shallowCopyArray); // 输出: [1, 2, 3, 4]
console.log(deepCopyArray); // 输出: [1, 2, 3, 4]

alt

需要注意的是,深拷贝可能会导致性能损耗,特别是对于大型和复杂的对象。因此,在需要进行深拷贝时,应根据实际需求和性能考虑来选择合适的方法。

6. 如何使用JSON.stringify和JSON.parse实现深拷贝?

可以使用 JSON.stringify()JSON.parse() 结合来实现一层深拷贝,对于没有循环引用的简单对象和数组是适用的。

这种方法的基本原理是将对象转换为 JSON 字符串,然后再将字符串解析回对象,这样就可以创建一个对象的副本。

以下是使用 JSON.stringify()JSON.p 实现深拷贝的示例:

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

前端面试必备 文章被收录于专栏

前端面试必备知识点:HTML和CSS、JS(变量/数据类型/操作符/条件语句/循环;面向对象编程/函数/闭包/异步编程/ES6)、DOM操作、HTTP和网络请求、前端框架、前端工具和构建流程、浏览器和性能优化、跨浏览器兼容性、前端安全、数据结构和算法、移动端开发技术、响应式设计、测试和调试技巧、性能监测等。准备面试时,建议阅读相关的技术书籍、参与项目实践、刷题和练习,以深化和巩固你的知识。

全部评论
function deepClone(obj, hash = new WeakMap()) {}
1 回复 分享
发布于 2023-09-06 22:48 广东
【处理深拷贝中的循环引用2】使用WeakMap进行引用记录:类似于哈希表的做法,使用WeakMap作为引用容器,将每个对象作为键,对应的拷贝对象作为值,进行深拷贝时,判断WeakMap中是否已经存在对应的键值对,存在则直接返回值,否则进行深拷贝并添加到WeakMap中。
1 回复 分享
发布于 2023-09-06 22:48 广东
【处理深拷贝中的循环引用】使用哈希表进行引用记录:在进行深拷贝时,创建一个空的哈希表,每次拷贝一个对象时,先判断该对象是否已经存在于哈希表中,如果存在,则直接返回其对应的拷贝对象;如果不存在,则将该对象添加到哈希表中,并进行深拷贝操作。
1 回复 分享
发布于 2023-09-06 22:47 北京
浅拷贝的定义是不是错了。浅拷贝出来的数据对象的地址不同的吧
点赞 回复 分享
发布于 2024-02-21 00:11 福建
深拷贝可能会面临循环引用的情况,即对象之间存在相互引用的关系。如果不正确处理循环引用,深拷贝可能会导致无限递归,最终导致堆栈溢出
点赞 回复 分享
发布于 2023-09-08 23:40 广东

相关推荐

点赞 评论 收藏
分享
评论
3
11
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务