JS:如何实现对象的拷贝

拷贝指的是将一个对象的内容复制到另一个对象中。在JavaScript中,拷贝指的是将一个对象的内容复制到另一个对象中,拷贝分为浅拷贝和深拷贝且通常只针对引用类型。浅拷贝只拷贝一层对象,它会复制原始值以及引用类型数据的指针。而深拷贝会层层拷贝,它会将所有类型的属性值都会被复制。

浅拷贝

浅拷贝是指通过方法创建一个新对象把某个对象完整拷贝,拷贝后的新对象中的每个元素都是原始对象中相应元素的引用,即原对象的修改会影响新的对象。

在JavaScript中,原始类型的值直接存在调用栈中而引用类型的值是存放在堆中的,引用类型在调用栈里存放的只是堆中的引用地址。所以当对原始类型进行拷贝时,实际上是将原始值从一个变量复制到另一个变量,而不涉及引用。

常见的浅拷贝方法

  • Object.create(obj) // 创建一个对象
css 复制代码
let a = {
    name:'张三'
}
let b =Object.create(a)
a.name='李四'

console.log(b.name); //输出: 李四
  • Object.assign({}, obj) // 对象拼接方法
css 复制代码
let a ={
    name:'张三',
    info:{
        age:18
    }
}
let b=Object.assign({},a)
a.info.age=20

console.log(b)//输出: { name: '张三', info: { age: 20 } }
  • \].concat(arr) // 数组拼接方法

let arr = [1,2,3,{n:10}]
let newArr = [].concat(arr)
arr[3].n =100

console.log(newArr);

复制代码
* 数组解构

```ini
let arr = [1,2,3,{n:10}]
let newArr=[...arr]
arr[3].n =100

console.log(newArr);
  • slice() //数组剪切方法
ini 复制代码
const arr = [1, 2, 3, 4, 5];
const newArr = arr.slice();

console.log(newArr); // [1, 2, 3, 4, 5]

浅拷贝的实现

ini 复制代码
let obj={
    a:1,
    b:{
        n:2
    },
    c:[1,2,3,4]
}

let obj2 = shallowCopy(obj);

function shallowCopy(obj) {
    let obj2 = {};
    for (let key in obj) {
        if(obj.hasOwnProperty(key)){
            obj2[key] = obj[key];
        }
    } 
    return obj2;
}

obj2.b.n=100
console.log(obj); // 输出为:{ a: 1, b: { n: 100 }, c: [ 1, 2, 3, 4 ] }
console.log(obj2);// 输出为:{ a: 1, b: { n: 100 }, c: [ 1, 2, 3, 4 ] }

深拷贝

深拷贝是指通过方法创建一个新对象把某个对象完整拷贝,拷贝后的新对象中的内容是复制的原始对象的内容,新对象中的每个元素都是原始对象中相应元素的复制。所以修改新对象中的元素不会影响到原始对象中的对应元素。

常见的深拷贝方法

  • JSON.parse(JSON.stringify(obj))

当使用JSON.parse(JSON.stringify(obj)) 对对象进行深拷贝时,会将对象转换为 JSON 字符串,然后再将 JSON 字符串转换回对象,从而实现深层次的拷贝。

javascript 复制代码
let obj = {
    name: "张三",
    info: {
        age: 25,
        sex: "男"
    }
};

// 使用 JSON.parse(JSON.stringify(obj)) 进行深拷贝
let obj2 = JSON.parse(JSON.stringify(obj));

// 修改新对象中的属性的值
obj2.info.age=18;

// 输出原始对象和克隆后的对象
console.log(obj); // 输出: { name: '张三', info: { age: 25, sex: '男' } }
console.log(obj2); // 输出: { name: '张三', info: { age: 18, sex: '男' } }

值得注意的是:

  1. 这个方法不能处理underfined,function,Symbol 这些数据类型
  2. 也无法处理循环引用 。

这个方法可以使用大部分情况,如果遇到以上两种问题可以使用lodash的深拷贝函数


  • structuredClone(obj)
ini 复制代码
let obj = {
    a: 1,
    b: {
        n: 2
    },
    c: [1, 2, 3, 4]
}

const obj2=structuredClone(obj) 

structuredClone() 方法是用于复制一个对象,包括其所有属性和嵌套对象,而不会受限于像 JSON.stringify() 和 JSON.parse() 那样只能处理一部分 JavaScript 对象的限制。 值得注意的是structuredClone(obj)无法兼容symbol 和function。


深拷贝的实现

  • 递归
javascript 复制代码
let obj = {
  name: '张三',
  age: 18,
  a: {
    n: 1
  },
  b: undefined,
  c: null,
  d: function () { },
  e: Symbol('hello'),
  f: {
    n: 100
  }
}

function deepCopy(obj) {
  let objCopy = {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      //区分obj[key]是原始类型或者引用类型
      if (obj[key] instanceof Object) {// 不能直接赋值
        objCopy[key] = deepCopy(obj[key]) // 递归调用 
      } else {
        objCopy[key] = obj[key]
      }
    }
  }
  return objCopy
}

let obj2 = deepCopy(obj)
obj.age = 20;

console.log(obj2); 

通过遍历对象的属性,并利用递归来处理嵌套对象,从而实现了对象的深拷贝。

  • MessageChannel()
javascript 复制代码
let obj = {
    a: 1,
    b: {
        n: 2
    },
    c: [1, 2, 3, 4]
}

function deepCLone(obj) {
    return new Promise((resolve) => {
        const { port1, port2 } = new MessageChannel();
        port1.postMessage(obj)
        port2.onmessage = (msg) => {
            resolve(msg.data)
        }
    })
}

deepCLone(obj).then((res) => {
    obj2 = res
    console.log(obj2);
})

在这个函数中,使用 MessageChannel 创建了两个端口 port1port2,然后利用其中一个端口将对象 obj 发送给另一个端口。当另一个端口接收到消息时,将其作为深拷贝后的对象返回。通过 MessageChannel 实现的深拷贝可以处理对象包含循环引用的情况。

总结

需要注意的是,深拷贝会消耗更多的内存和时间,而在处理简单对象结构、不需要深层嵌套对象的情况下,浅拷贝是一个简单而有效的工具。总之,在进行拷贝操作时,需要根据实际情况选择合适的方式来确保程序的正确性和性能。

相关推荐
Qredsun几秒前
JS-OCR-demo加载本地文件
开发语言·javascript·ocr
黄同学real10 分钟前
Vue2 相关知识点整理
前端·vue.js
哎哟喂_!14 分钟前
在 UniApp 中实现 App 与 H5 页面的跳转及通信
前端·javascript·vue.js·uni-app
^小桃冰茶39 分钟前
HTML 的基本结构与简单文件编写方法
前端·html
生产队队长42 分钟前
HTML:常用标签(元素)汇总
前端
南客先生1 小时前
Arthas在Java程序监控和分析中的应用
java·面试·arthas
森叶1 小时前
大前端开发——前端知识渐变分层讲解 & 利用金字塔原理简化前端知识体系
前端·webpack·electron
曹天骄1 小时前
免费 Mock 图片 Mock 地址清单
javascript
有被蠢哭到2 小时前
SQL面试之--明明建了索引为什么失效了?
数据库·sql·面试
黄同学real2 小时前
常见的 CSS 知识点整理
前端·css