Javascript 使用扩展语法在 ES6 中进行深度复制

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38416020/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 21:28:13  来源:igfitidea点击:

Deep copy in ES6 using the spread syntax

javascriptecmascript-6reduxspread-syntax

提问by Guy

I am trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.

我正在尝试为我的 Redux 项目创建一个深拷贝映射方法,该方法将使用对象而不是数组。我读到在 Redux 中,每个状态都不应该改变先前状态中的任何内容。

export const mapCopy = (object, callback) => {
    return Object.keys(object).reduce(function (output, key) {

    output[key] = callback.call(this, {...object[key]});

    return output;
    }, {});
}

It works:

有用:

    return mapCopy(state, e => {

            if (e.id === action.id) {
                 e.title = 'new item';
            }

            return e;
        })

However it does not deep copy inner items so I need to tweak it to:

但是它不会深度复制内部项目,因此我需要将其调整为:

export const mapCopy = (object, callback) => {
    return Object.keys(object).reduce(function (output, key) {

    let newObject = {...object[key]};
    newObject.style = {...newObject.style};
    newObject.data = {...newObject.data};

    output[key] = callback.call(this, newObject);

    return output;
    }, {});
}

This is less elegant as it requires to know which objects are passed. Is there a way in ES6 to use the spread syntax to deep copy an object?

这不太优雅,因为它需要知道传递了哪些对象。ES6 中有没有办法使用扩展语法来深度复制对象?

采纳答案by Frank Tan

No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.

ES6 没有内置这样的功能。我认为您有几种选择,具体取决于您想要做什么。

If you really want to deep copy:

如果你真的想深拷贝:

  1. Use a library. For example, lodash has a cloneDeepmethod.
  2. Implement your own cloning function.
  1. 使用图书馆。例如,lodash 有一个cloneDeep方法。
  2. 实现自己的克隆功能。

Alternative Solution To Your Specific Problem (No Deep Copy)

您的特定问题的替代解决方案(无深度复制)

However, I think, if you're willing to change a couple things, you can save yourself some work. I'm assuming you control all call sites to your function.

然而,我认为,如果你愿意改变一些事情,你可以为自己节省一些工作。我假设您控制所有调用站点到您的功能。

  1. Specify that all callbacks passed to mapCopymust return new objects instead of mutating the existing object. For example:

    mapCopy(state, e => {
      if (e.id === action.id) {
        return Object.assign({}, e, {
          title: 'new item'
        });
      } else {  
        return e;
      }
    });
    

    This makes use of Object.assignto create a new object, sets properties of eon that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.

  2. mapCopycan be really simple now:

    export const mapCopy = (object, callback) => {
      return Object.keys(object).reduce(function (output, key) {
        output[key] = callback.call(this, object[key]);
        return output;
      }, {});
    }
    
  1. 指定传递给的所有回调mapCopy必须返回新对象,而不是改变现有对象。例如:

    mapCopy(state, e => {
      if (e.id === action.id) {
        return Object.assign({}, e, {
          title: 'new item'
        });
      } else {  
        return e;
      }
    });
    

    这使用Object.assign来创建一个新对象,e在该新对象上设置属性,然后在该新对象上设置新标题。这意味着您永远不会改变现有对象,而仅在必要时创建新对象。

  2. mapCopy现在可以很简单:

    export const mapCopy = (object, callback) => {
      return Object.keys(object).reduce(function (output, key) {
        output[key] = callback.call(this, object[key]);
        return output;
      }, {});
    }
    

Essentially, mapCopyis trusting its callers to do the right thing. This is why I said this assumes you control all call sites.

从本质上讲,mapCopy是相信调用者会做正确的事情。这就是为什么我说这假设您控制所有呼叫站点。

回答by Nikhil Mahirrao

Instead use this for deep copy

而是将其用于深层复制

var newObject = JSON.parse(JSON.stringify(oldObject))

var oldObject = {
  name: 'A',
  address: {
    street: 'Station Road',
    city: 'Pune'
  }
}
var newObject = JSON.parse(JSON.stringify(oldObject));

newObject.address.city = 'Delhi';
console.log('newObject');
console.log(newObject);
console.log('oldObject');
console.log(oldObject);

回答by Mina Luke

From MDN

来自 MDN

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).

注意:Spread 语法在复制数组时有效地深入一层。因此,它可能不适合复制多维数组,如下例所示(与 Object.assign() 和 spread 语法相同)。

Personally, I suggest using Lodash's cloneDeepfunction for multi-level object/array cloning.

就个人而言,我建议使用Lodash 的 cloneDeep函数进行多级对象/数组克隆。

Here is a working example:

这是一个工作示例:

const arr1 = [{ 'a': 1 }];

const arr2 = [...arr1];

const arr3 = _.clone(arr1);

const arr4 = arr1.slice();

const arr5 = _.cloneDeep(arr1);

const arr6 = [...{...arr1}]; // a bit ugly syntax but it is working!


// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false

// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

回答by HectorGuo

I often use this:

我经常使用这个:

function deepCopy(obj) {
    if(typeof obj !== 'object' || obj === null) {
        return obj;
    }

    if(obj instanceof Date) {
        return new Date(obj.getTime());
    }

    if(obj instanceof Array) {
        return obj.reduce((arr, item, i) => {
            arr[i] = deepCopy(item);
            return arr;
        }, []);
    }

    if(obj instanceof Object) {
        return Object.keys(obj).reduce((newObj, key) => {
            newObj[key] = deepCopy(obj[key]);
            return newObj;
        }, {})
    }
}

回答by Shashidhar Reddy

const a = {
  foods: {
    dinner: 'Pasta'
  }
}
let b = JSON.parse(JSON.stringify(a))
b.foods.dinner = 'Soup'
console.log(b.foods.dinner) // Soup
console.log(a.foods.dinner) // Pasta

Using JSON.stringifyand JSON.parseis the best way. Because by using the spread operator we will not get the efficient answer when the json object contains another object inside it. we need to manually specify that.

使用JSON.stringifyJSON.parse是最好的方法。因为通过使用扩展运算符,当 json 对象包含另一个对象时,我们将无法得到有效的答案。我们需要手动指定。

回答by user10919042

// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
  // return non object values
  if('object' !==typeof o) return o
  // m: a map of old refs to new object refs to stop recursion
  if('object' !==typeof m || null ===m) m =new WeakMap()
  var n =m.get(o)
  if('undefined' !==typeof n) return n
  // shallow/leaf clone object
  var c =Object.getPrototypeOf(o).constructor
  // TODO: specialize copies for expected built in types i.e. Date etc
  switch(c) {
    // shouldn't be copied, keep reference
    case Boolean:
    case Error:
    case Function:
    case Number:
    case Promise:
    case String:
    case Symbol:
    case WeakMap:
    case WeakSet:
      n =o
      break;
    // array like/collection objects
    case Array:
      m.set(o, n =o.slice(0))
      // recursive copy for child objects
      n.forEach(function(v,i){
        if('object' ===typeof v) n[i] =clone(v, m)
      });
      break;
    case ArrayBuffer:
      m.set(o, n =o.slice(0))
      break;
    case DataView:
      m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
      break;
    case Map:
    case Set:
      m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
      break;
    case Int8Array:
    case Uint8Array:
    case Uint8ClampedArray:
    case Int16Array:
    case Uint16Array:
    case Int32Array:
    case Uint32Array:
    case Float32Array:
    case Float64Array:
      m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
      break;
    // use built in copy constructor
    case Date:
    case RegExp:
      m.set(o, n =new (c)(o))
      break;
    // fallback generic object copy
    default:
      m.set(o, n =Object.assign(new (c)(), o))
      // recursive copy for child objects
      for(c in n) if('object' ===typeof n[c]) n[c] =clone(n[c], m)
  }
  return n
}

回答by Harish Sekar

const cloneData = (dataArray) => {
    newData= []
    dataArray.forEach((value) => {
        newData.push({...value})
    })
    return newData
}
  • a = [{name:"siva"}, {name:"siva1"}] ;
  • b = myCopy(a)
  • b === a // false`
  • a = [{name:"siva"}, {name:"siva1"}] ;
  • b = myCopy(a)
  • b === a // 假`

回答by Leonid Titov

I myself landed on these answers last day, trying to find a way to deep copy complex structures, which may include recursive links. As I wasn't satisfied with anything being suggested before, I implemented this wheel myself. And it works quite well. Hope it helps someone.

昨天我自己找到了这些答案,试图找到一种深度复制复杂结构的方法,其中可能包括递归链接。由于我对之前提出的任何建议都不满意,因此我自己实现了这个轮子。而且效果很好。希望它可以帮助某人。

Example usage:

用法示例:

OriginalStruct.deep_copy = deep_copy; // attach the function as a method

TheClone = OriginalStruct.deep_copy();

Please look at https://github.com/latitov/JS_DeepCopyfor live examples how to use it, and also deep_print() is there.

请查看https://github.com/latitov/JS_DeepCopy以获取如何使用它的实时示例,还有 deep_print() 。

If you need it quick, right here's the source of deep_copy() function:

如果您需要快速,这里是 deep_copy() 函数的来源:

function deep_copy() {
    'use strict';   // required for undef test of 'this' below

    // Copyright (c) 2019, Leonid Titov, Mentions Highly Appreciated.

    var id_cnt = 1;
    var all_old_objects = {};
    var all_new_objects = {};
    var root_obj = this;

    if (root_obj === undefined) {
        console.log(`deep_copy() error: wrong call context`);
        return;
    }

    var new_obj = copy_obj(root_obj);

    for (var id in all_old_objects) {
        delete all_old_objects[id].__temp_id;
    }

    return new_obj;
    //

    function copy_obj(o) {
        var new_obj = {};
        if (o.__temp_id === undefined) {
            o.__temp_id = id_cnt;
            all_old_objects[id_cnt] = o;
            all_new_objects[id_cnt] = new_obj;
            id_cnt ++;

            for (var prop in o) {
                if (o[prop] instanceof Array) {
                    new_obj[prop] = copy_array(o[prop]);
                }
                else if (o[prop] instanceof Object) {
                    new_obj[prop] = copy_obj(o[prop]);
                }
                else if (prop === '__temp_id') {
                    continue;
                }
                else {
                    new_obj[prop] = o[prop];
                }
            }
        }
        else {
            new_obj = all_new_objects[o.__temp_id];
        }
        return new_obj;
    }
    function copy_array(a) {
        var new_array = [];
        if (a.__temp_id === undefined) {
            a.__temp_id = id_cnt;
            all_old_objects[id_cnt] = a;
            all_new_objects[id_cnt] = new_array;
            id_cnt ++;

            a.forEach((v,i) => {
                if (v instanceof Array) {
                    new_array[i] = copy_array(v);
                }
                else if (v instanceof Object) {
                    new_array[i] = copy_object(v);
                }
                else {
                    new_array[i] = v;
                }
            });
        }
        else {
            new_array = all_new_objects[a.__temp_id];
        }
        return new_array;
    }
}

Cheers@!

干杯@!

回答by Jeroen Breen

function deepclone(obj) {
    let newObj = {};

    if (typeof obj === 'object') {
        for (let key in obj) {
            let property = obj[key],
                type = typeof property;
            switch (type) {
                case 'object':
                    if( Object.prototype.toString.call( property ) === '[object Array]' ) {
                        newObj[key] = [];
                        for (let item of property) {
                            newObj[key].push(this.deepclone(item))
                        }
                    } else {
                        newObj[key] = deepclone(property);
                    }
                    break;
                default:
                    newObj[key] = property;
                    break;

            }
        }
        return newObj
    } else {
        return obj;
    }
}

回答by ganesh phirke

Here is the deepClone function which handles all primitive, array, object, function data types

这是处理所有原始、数组、对象、函数数据类型的 deepClone 函数

function deepClone(obj){
 if(Array.isArray(obj)){
  var arr = [];
  for (var i = 0; i < obj.length; i++) {
   arr[i] = deepClone(obj[i]);
  }
  return arr;
 }

 if(typeof(obj) == "object"){
  var cloned = {};
  for(let key in obj){
   cloned[key] = deepClone(obj[key])
  }
  return cloned; 
 }
 return obj;
}

console.log( deepClone(1) )

console.log( deepClone('abc') )

console.log( deepClone([1,2]) )

console.log( deepClone({a: 'abc', b: 'def'}) )

console.log( deepClone({
  a: 'a',
  num: 123,
  func: function(){'hello'},
  arr: [[1,2,3,[4,5]], 'def'],
  obj: {
    one: {
      two: {
        three: 3
      }
    }
  }
}) )