如何深度复制一个javascript对象

Posted 云中桥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何深度复制一个javascript对象相关的知识,希望对你有一定的参考价值。

前言

最近有人问我,如何将一个对象复制一份,因为他遇到了一个需求,需要将后端获取的数据,保存一份,原始数据会因为交互而发生变化,最终需要对比两份数据的异同。
他是一个刚入行的小朋友,他的实现方式就是新声明了一个变量,然后将数据赋值给了变量。本以为这就ok了,结果修改原数据,复制出来的变量中的内容,依然发生了变化。
在此,整理一下。(大中小)牛略过,仅为帮助新人,聊以解忧。

 

知识铺垫,值类型,与引用类型

我们用一个新的概念来理解这两种类型,连锁店连锁店的钥匙

1. 类型包含如下:number,string,boolean,undefind,null;

    变量的交换如同新开了一家连锁店,店面统一,但实际上新店与旧点之间,互无影响,各自运营,独立结算。 

 

var flagship  = ‘xx连锁店‘; //当前为旗舰店
var shop1 = flagship;      //第一家连锁店
var shop2 = flagship;      //第二家连锁店

 

2. 引用类型包含如下:对象,数组,函数

    变量的交换等于现有一家店面的钥匙(变量地址引用)复制给了另外一个老板,此时两个人共同管理一家门店,两个人的行为都可能对着一间门店的运营造成影响。

var boss1 = {shop:‘xx连锁店‘,‘state‘:‘开业中‘}
var boss2 = boss1;//复制了一把钥匙给boss2
boss2.state = ‘装修中‘; //boss2将门店升级改造,暂时停业
console.log(boss1.state) // 装修中 

由此可见,对应用类型的变量仅仅通过变量赋值,无法达到深拷贝的意图,仅仅只是浅拷贝。

 

如何去做呢

以下简单实现一个对纯数据对象(JSON)的深拷贝

1.实现值类型的复制:

从上面我们知道,值类型可以简单的通过变量赋值来实现深拷贝,所以先完成如下代码:

function clone(item) {
  var type = typeof item, 
     baseTypes = [‘boolean‘,‘number‘,‘string‘,‘undefined‘],
     result;   
//使用typeof 可以准确判断出 boolean string number undefined   //null 使用全等方式进行判断   if(baseTypes.indexOf(type) >= 0 || item === null){      result = item;   }
  return result; }

2.实现引用类型中数组的复制:

首先需要判断是否为数组,其次将数组进行遍历,分别复制数组中的内容,暂时不考虑数组中元素包含引用类型

function clone(item) {
  var result;
  //判断当前元素是否为数组,至于为什么这么判断,不深入了,有兴趣自己百度吧~
  if(Object.prototype.toString.call(item) === "[object Array]"){
    result = [];
    //循环数组,将数组内容放到新数组中(未考虑数组中元素是否包含引用类型)
    item.forEach(function (i) {
      result.push(i);
    })
  }
  return result;
}

3.实现引用类型中对象的复制:

首先需要判断是否为对象,其次将对象进行遍历,分别复制对象中的内容,暂时不考虑数组中元素包含引用类型

function clone1(item) {
  var result;
  //判断当前元素是否为对象,至于为什么这么判断,不深入了,有兴趣自己百度吧~
  if(Object.prototype.toString.call(item) === "[object Object]"){
    result = {};
    //遍历对象,将对象中内容放到新对象中(未考虑对象中元素是否包含引用类型)
    for(var i in item){
      result[i] = item[i]; 
    }
  }
  return result;
}

4.将对象或数组中包含引用类型值的情况考虑进去,一个简单的递归调用,整体代码如下

function clone(item) {
  var type = typeof item,
    baseTypes = [‘boolean‘,‘number‘,‘string‘,‘undefined‘],
    result;
  //使用typeof 可以准确判断出 boolean string number undefined
  //null 使用全等方式进行判断
  if(baseTypes.indexOf(type) >= 0 || item === null){
    result = item;
  }else if(Object.prototype.toString.call(item) === "[object Array]"){ // 判断是否为数组
    result = [];
    //循环数组,将数组内容放到新数组中
    item.forEach(function (i) {
      result.push(clone(i));
    })
  }else if(Object.prototype.toString.call(item) === "[object Object]"){ // 判断是否为对象
    result = {};
    //遍历对象,将对象中内容放到新对象中(未考虑对象中元素是否包含引用类型)
    for(var i in item){
      result[i] = clone(item[i]);
    }
  }else{
    result = item;
  }
    return result;
}

 

PS:还有没有其他简便方法呢

我们知道JSON,本身提供两个方法 JSON.stringify() 和 JSON.parse(),提供了将JSON对象转换为JSON结构的字符串,以及将JSON结构的字符串换换为JSON

so:简便方法来了

function clone(item) {
  var jsonStr = JSON.stringify(item);
  return JSON.parse(jsonStr);
}

 

以上是关于如何深度复制一个javascript对象的主要内容,如果未能解决你的问题,请参考以下文章

如何深度复制一个javascript对象

在 Javascript 中,当执行深度复制时,由于属性是“this”,我如何避免循环?

JavaScript 中的对象深度复制(Object Deep Clone)

在javascript中将对象数组复制到另一个数组中(深度复制)

关于JavaScript对象深度克隆

VSCode自定义代码片段12——JavaScript的Promise对象