关于前端的深拷贝与浅拷贝及深拷贝常用的几种方式

K ° 2024-10-06 09:03:01 阅读 94

前言

        不知不觉在代码行业已经任职四年多了,自己也从一开始的职场小白变成了现在的老油条。写这篇文章也是因为最近一些工作问题需要在CSDN上面发文,突然就想自己也写着玩一下,正好前段时间有个后端的朋友在学习前端代码时遇到了深拷贝的问题,那就简单的来说一下这个问题。

正文

        步入正题: 深拷贝与浅拷贝,这是前端人员在很多时候都会遇到的一个问题。最主要的展现形式就是一个对象或数组我明明没有给他操作,它却突然发生了变化。或某个对象我明明进行了操作,它却又还原了。这些都是使用前端的深拷贝、浅拷贝不正确经常造成的问题

        首先,先来了解一下浅拷贝。在前端书写时,我们必不可少的会遇到赋值的问题。

一、浅拷贝

        如下面三种

<code> let Number1 = 123456;

let Number2 = Number1

let obj1 = { a: 1, b: 2 };

let obj2 = Object.assign({}, obj1);

let arr1 = [1, 2, 3];

let arr2 = [...arr1];

以上三种都是明显的浅拷贝方法。浅拷贝的方法是我们前端最常用的一种复制对象的方式,因为它有个很大的优点就是

        简单!简单!简单!   重要的事情说三遍。

而且浅拷贝的性能是比较好的,当然,这跟它只复制第一层属性有关。

但是很多朋友在进行简单赋值了以后会发现一个很神奇的问题,就是我明明修改的是我复制出来的元素,但是,我一开始保存的初始数据也发生了变化,这就不得不提到浅拷贝的定义了:

浅拷贝创建一个新的对象或数组,但是新对象或数组中的元素是对原始对象或数组中对应元素的引用

注意最后面的引用二字。也就是说,我复制出来的元素的最终指向,它还是指向的一开始的元素。所以,当后面我修改元素的数据后,也就是修改了我指向的元素数据。

let obj1 = { {a:1}, {b:2}, {c:3}};

let obj2 = obj1; // obj2 = { {a:1}, {b:2}, {c:3}}

obj2.b = 5; //obj2 = { {a:1}, {b:5}, {c:3}}

console.log(obj1, obj2); // obj1 = { {a:1}, {b:5}, {c:3}} obj2 = { {a:1}, {b:5}, {c:3}}

所以如果是在不需要完全独立的副本环境下,浅拷贝是一种可以优先考虑的方法。

二、深拷贝

        但是如果需要我们需要完全独立的数据,那就需要考虑另外一种深拷贝了。

        首先我们要明确一点,深拷贝与浅拷贝最大的区别就在于复制出来元素的指向。

        当深拷贝出来的元素完全独立出来的,那无论我们进行什么操作都不会影响到初始数据的。

        简单列举下常用的几种深拷贝方法:

        1、执行递归方法实现深拷贝

let obj1 = {

name: 'name',

objson: { a:111, b:222 },

func: function () {},

arr: [ 1, 2, 3 ]

}

function handleCopy(obj) {

let obj_new

if (typeof obj !== 'object' || obj === null) {

return obj;

}

if (Array.isArray(obj)) {

obj_new = [];

for (let i = 0; i < obj.length; i++) {

obj_new[i] = handleCopy(obj[i]);

}

} else {

obj_new = {};

for (let key in obj) {

if (obj.hasOwnProperty(key)) {

obj_new[key] = deepCopy(obj[key]);

}

}

}

return obj_new;

}

let obj2 = handleCopy(obj1)

通过以上方法转化后,不管怎样修改obj2内的数据,obj1中的数据都不会发生变化。

        2、JOSN转化的方法

        通过JSON的序列化和反序列化方法来实现深拷贝的功能,通过方法可以深拷贝大部分的数据类型,但是要注意一点,这个方法不可以拷贝对象中的方法和undefined,反而会把两种属性丢失。因为JSON是一种数据交换格式,不能表示函数、undefined或循环引用。

let obj = {

name: 'name',

date: new Date(),

err: new Error('error message'),

func: function() { console.log('func') },

unde: undefined,

nan: NaN

}

let obj_new = JSON.parse(JSON.stringify(obj));

console.log('obj_new ', obj_new );

// obj_new = {

// name: 'name',

// date: '****-**-**T**:**:**.***Z'

// err: {},

// nan: null

// }

注意:如果原数据中存在循环引用,那方法会直接进行报错。

        3、使用第三方库封装的方法

        现在很多第三方库中都有封装好的深拷贝的方法,比如  lodash  中的  cloneDeep  方法

const _ = require('lodash');

let obj = {

name: 'name'

arr: [1, 2, 3],

objson: {a: 1, b:2, c:3}

}

let obj_new = _.cloneDeep(obj);

引入方法的确是快速方便,但是因为一个深拷贝单独的去引入一个封装库就有点舍本逐末了,尤其是当方法发生了变化还需要去看第三方库的代码,所以也不是很推荐。

        4、extend()方法进行深拷贝

        这种方法仅推荐在JQ中使用,这个方法跟第三种原理类似,但是JQuery的适用性对比其他是超出很多的。它自身携带的extend()基本不存在什么坑,也省略了自己写递归的麻烦步骤,基本可以放心使用。

let obj = {

name: 'name'

arr: [1, 2, 3],

objson: {a: 1, b:2, c:3}

}

let obj_new = $.extend(true, {}, obj);

以上就是我经常用到的深拷贝方法了,个人还是比较推荐自己封装一个递归方法的,方便修改,而且想用就用。当然简单的拷贝的话也可用JSON方法,就是不知道什么时候可能就会碰到坑。

        最后,愿世界永无bug!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。