前端面试题---深拷贝、浅拷贝的实现和解构赋值

一.如何在 JavaScript 中实现深拷贝和浅拷贝?

在 JavaScript 中实现深拷贝和浅拷贝可以采用不同的方法。下面分别介绍这两种拷贝方式的实现方式

1.浅拷贝(Shallow Copy)

浅拷贝(shallow copy)是一种复制对象或数组的操作,创建一个新的对象或数组,并将原始对象或数组的属性或元素的引用复制到新的对象或数组中。这意味着原始对象和拷贝对象会共享相同的引用类型数据,而不是创建新的引用类型数据。

浅拷贝只复制对象或数组的一层属性或元素,而不会递归地复制嵌套的对象或数组。当修改拷贝对象的属性或元素时,原始对象的对应属性或元素也会受到影响。因此,浅拷贝适用于简单的数据结构,但不适用于复杂的嵌套结构。

下面介绍几种常见的浅拷贝方法:

扩展运算符(Spread Operator)使用扩展运算符可以快速进行浅拷贝,将原始对象的属性或数组的元素展开到新的对象或数组中。

const originalObject = { name: 'John', age: 30 };
const shallowCopy = { ...originalObject };
//output:{ name: 'John', age: 30 }

const originalArray = [1, 2, 3];
const shallowCopy = [...originalArray];
//output:[1, 2, 3]

 Object.assign() 方法 使用 Object.assign() 方法可以将原始对象的属性复制到新的对象中。

const originalObject = { name: 'John', age: 30 };
const shallowCopy = Object.assign({}, originalObject);

const originalArray = [1, 2, 3];
const shallowCopy = Object.assign([], originalArray);

 Array.prototype.slice() 方法  对于数组,可以使用 slice() 方法来创建一个新的数组,并将原始数组的元素复制到新的数组中。

const originalArray = [1, 2, 3];
const shallowCopy = originalArray.slice();

2.深拷贝(Deep Copy)

深拷贝(deep copy)是一种复制对象或数组的操作,创建一个全新的对象或数组,并递归地复制原始对象或数组的所有属性或元素,包括嵌套的对象或数组。深拷贝创建的副本是完全独立的,对副本的修改不会影响原始对象或数组。

深拷贝适用于复杂的数据结构,包括嵌套的对象或数组。当需要对数据进行修改、操作或传递给其他函数时,深拷贝可以确保原始数据的完整性和不变性。

 下面介绍几种常见的深拷贝方法:

 递归复制

通过递归遍历对象或数组,并复制所有的属性或元素,可以实现深拷贝。

function deepCopy(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj; // 非对象类型直接返回
  }
  
  const copy = Array.isArray(obj) ? [] : {};
  
  for (let key in obj) {
    if (Object.hasOwnProperty.call(obj, key)) {
      copy[key] = deepCopy(obj[key]);
    }
  }
  
  return copy;
}

const originalObject = { name: 'John', age: 30 };
const deepCopyObject = deepCopy(originalObject);

const originalArray = [1, 2, { nested: true }];
const deepCopyArray = deepCopy(originalArray);

JSON 序列化与反序列化

可以使用 JSON 对象的 stringify() 方法将原始对象或数组转换为 JSON 字符串,再使用 parse() 方法将 JSON 字符串转换为新的对象或数组。这种方法可以实现简单的深拷贝,但有一些限制,例如无法复制函数、正则表达式和特殊的对象类型。

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

const originalArray = [1, 2, { nested: true }];
const deepCopyArray = JSON.parse(JSON.stringify(originalArray));

需要注意的是,使用 JSON 序列化与反序列化的方式进行深拷贝时,原始对象或数组中的函数、正则表达式、特殊对象类型等会被忽略或转换为字符串,丢失原始的数据类型和行为。

无论使用哪种深拷贝方法,都要注意以下几点:

  • 循环引用:如果原始对象或数组存在循环引用(即某个属性或元素引用了对象或数组本身),在深拷贝过程中可能导致无限递归,需要额外的处理来解决循环引用问题。
  • 性能考虑:深拷贝可能涉及递归遍历大量的数据,对于大型对象或数组,深拷贝的性能可能较低。在处理大型数据时,可以考虑使用其他优化的深拷贝库或算法,如 Lodash cloneDeep() 方法。
const originalObject = { name: 'John', age: 30 };
const deepCopyObject = _.cloneDeep(originalObject);

const originalArray = [1, 2, { nested: true }];
const deepCopyArray = _.cloneDeep(originalArray);

二.数据解构

解构赋值(destructuring assignment)是一种通过解构语法将数组或对象的属性值赋给变量的方式。它在 JavaScript 中引入了一种简洁而强大的方式来从复杂的数据结构中提取值。

解构赋值可以应用于数组和对象,并且支持多种用法和语法。下面详细介绍解构赋值的各种用法

 数组解构赋值

通过数组解构赋值,可以将数组的元素值分别赋给对应的变量。

const [a, b, c] = [1, 2, 3];
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 3

可以省略数组中的某些元素,只提取需要的部分。

const [a, , c] = [1, 2, 3];
console.log(a); // 输出 1
console.log(c); // 输出 3

还可以使用默认值来处理在解构过程中未定义的值。

const [a, b, c = 0] = [1, 2];
console.log(a); // 输出 1
console.log(b); // 输出 2
console.log(c); // 输出 0(使用默认值)

对象解构赋值

通过对象解构赋值,可以从对象中提取属性值并赋给对应的变量。

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

可以使用别名来为提取的属性值指定新的变量名。

const { name: personName, age: personAge } = { name: 'John', age: 30 };
console.log(personName); // 输出 'John'
console.log(personAge); // 输出 30

也可以使用默认值处理在解构过程中未定义的属性值。

const { name, age = 0 } = { name: 'John' };
console.log(name); // 输出 'John'
console.log(age); // 输出 0(使用默认值)

函数参数的解构赋值

可以在函数参数中使用解构赋值,从传入的对象或数组中提取所需的值。

function greet({ name, age }) {
  console.log(`Hello, ${name}! You are ${age} years old.`);
}

const person = { name: 'John', age: 30 };
greet(person); // 输出 'Hello, John! You are 30 years old.'

其他高级用法

剩余运算符(Rest Operator):可以使用剩余运算符 ... 将剩余的数组元素或对象属性提取为一个新的数组或对象。

const [a, ...rest] = [1, 2, 3, 4, 5];
console.log(a); // 输出 1
console.log(rest); // 输出 [2, 3, 4, 5]

const { name, ...rest } = { name: 'John', age: 30, city: 'New York' };
console.log(name); // 输出 'John'
console.log(rest); // 输出 { age: 30, city: 'New York' }

嵌套解构赋值:可以在数组和对象的解构赋值中进行嵌套,以提取多层嵌套的值。

function greet({ name, details: { age } }) {
  console.log(`Hello, ${name}! You are ${age} years old.`);
}

const person = { name: 'John', details: { age: 30 } };
greet(person); // 输出 'Hello, John! You are 30 years old.'

解构赋值的嵌套用法可以在函数参数中灵活应用,以提取更复杂的数据结构。

function greet({ name, details: { age } }) {
  console.log(`Hello, ${name}! You are ${age} years old.`);
}

const person = { name: 'John', details: { age: 30 } };
greet(person); // 输出 'Hello, John! You are 30 years old.'

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值