切换语言为:繁体

JavaScript 中你认为的深拷贝真的够「深」吗?

  • 爱糖宝
  • 2024-06-26
  • 2068
  • 0
  • 0

前言

在我们日常编码的时候,经常会碰到一种情况,就是要拷贝某个对象(在js中拷贝只讨论在引用类型,也就是对象上,因为js中基本类型的拷贝直接赋值就好了),拿到一个“一模一样”的“新”对象,但是在js中,很多种方式的拷贝并不能有效地拿到这个“新”对象,<>而只是简单地拿到了这个对象的引用地址,这样的引用就有可能会影响到原对象的属性,并不能满足拿到一个一模一样的“新”对象这个需求,我们称这种拷贝为浅拷贝
深拷贝则相反,是能够真正拿到一个“一模一样”并且使用起来不影响原对象的的对象。

今天我们就来具体讲讲浅拷贝和深拷贝的实现吧。。

浅拷贝(Shallow Copy)

实际上js中大部分方法对拷贝的实现都是浅拷贝

  • shallowCopy原理
    --先创建一个新对象newObj
    --用for..in..遍历原对象上的属性加入到新对象中
    --借助hasOwnProperty规避原对象隐式具有的属性

1、使用循环遍历对象的属性(将对象属性一个一个赋值到新对象)

function shallowCopy(obj) {
2  let newObj = {};
3  for(let key in obj) {
4    if(obj.hasOwnProperty(key)) {
5      newObj[key] = obj[key];
6    }
7  }
8  return newObj;
9}

2、利用Object.create()方法,以原对象作为实参传递给该方法,创建出一个新对象。

let newObj = Object.create(obj)

3、使用Object.assign()方法 object.assign()方法是es6引用可以用来合并对象用的,同样可以实现浅拷贝,只要将原对象和一个空对象合并就好了

let obj = {a: 1, b: {c: 2}};
let newObj = Object.assign({}, obj);

4、数组的Array.prototype.slice()Array.from() 对数组进行浅拷贝

let array = [1, 2, {a: 3}]; 
let newArray = array.slice(); 
// 或 
let newArray = Array.from(array);

5、使用展开运算符 ...,进行解构赋值。

let array = [1, 2, {a: 3}]; 
let newArray = [...array];   //数组
let obj = {a: 1, b: {c: 2}}; 
let newObj = {...obj};       //对象

这是一些在js中常用的浅拷贝的方法,浅拷贝所得到的对象,可能会在进行一些数值操作时,会影响到原对象的值,所以当我们需要得到一个一模一样的对象,但是不希望它影响原对象时,这就要用到深拷贝 了。

深拷贝(Deep Copy)

在原生js中我们要实现深拷贝,并且是没有对象属性的限制,我们要用到递归拷贝,思路:为递归对象中的所有属性,如果碰到基本数据类型,则直接赋值,若碰到对象,则递归进入,直到找到基本数据类型。

实现代码如下:

function deepCopy(obj){
    let newObj = {}
    for (let key in obj) {
        //key 是不是 obj 显示具有的,隐式的不copy
        if (obj.hasOwnProperty(key)) {
            if(obj[key] instanceof Object){
              newObj[key] = deepCopy(obj[key])
            }else {
            newObj[key] = obj[key]
            }
        }
    }
    return newObj
}

当然还有其他的深拷贝方法
使用JSON对象的 stringifyparse 方法
这种方法简单,但有限制,如无法处理函数、RegExp、Date、undefined等类型,且会丢失原型链上的属性。

function simpleDeepClone(obj) {
return JSON.parse(JSON.stringify(obj)); 
}

借助第三方库
使用如lodash的 _.cloneDeep 方法,这是一个强大的深拷贝实现,能够处理各种复杂的对象结构,包括循环引用。

const _ = require('lodash'); 
let clonedObj = _.cloneDeep(originalObj);

使用扩展运算符结合递归(仅适用于数组或扁平对象)
对于数组,可以结合扩展运算符和递归来实现深拷贝,但这种方法不适用于含有嵌套对象的复杂结构。

function deepCloneArray(arr) {
return arr.map(item => Array.isArray(item) ? deepCloneArray(item) : item);
}

小结

选择拷贝类型

  • 如果数据结构简单,不包含嵌套的对象或数组,或者你希望修改拷贝对象时原对象也跟着改变,可以选择浅拷贝。

  • 当你需要完全独立的数据副本,尤其是在处理复杂数据结构时,深拷贝是更合适的选择。

理解JavaScript中的拷贝机制对于避免意外的数据修改和确保数据的正确传递至关重要。在实际开发中,根据具体情况选择合适的拷贝方式是基本技能之一。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.