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) // 数组拼接方法
ini 复制代码
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 实现的深拷贝可以处理对象包含循环引用的情况。

总结

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

相关推荐
柏箱3 分钟前
PHP基本语法总结
开发语言·前端·html·php
新缸中之脑12 分钟前
Llama 3.2 安卓手机安装教程
前端·人工智能·算法
hmz85616 分钟前
最新网课搜题答案查询小程序源码/题库多接口微信小程序源码+自带流量主
前端·微信小程序·小程序
看到请催我学习22 分钟前
内存缓存和硬盘缓存
开发语言·前端·javascript·vue.js·缓存·ecmascript
blaizeer1 小时前
深入理解 CSS 浮动(Float):详尽指南
前端·css
速盾cdn1 小时前
速盾:网页游戏部署高防服务器有什么优势?
服务器·前端·web安全
小白求学11 小时前
CSS浮动
前端·css·css3
什么鬼昵称2 小时前
Pikachu-csrf-CSRF(POST)
前端·csrf
XiaoYu20022 小时前
22.JS高级-ES6之Symbol类型与Set、Map数据结构
前端·javascript·代码规范
golitter.2 小时前
Vue组件库Element-ui
前端·vue.js·ui