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

Guy*_*Guy 79 javascript ecmascript-6 spread-syntax redux

我正在尝试为我的Redux项目创建一个深度复制映射方法,该方法将使用对象而不是数组.我读到在Redux中,每个州都不应该改变以前的状态.

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

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

    return output;
    }, {});
}
Run Code Online (Sandbox Code Playgroud)

有用:

    return mapCopy(state, e => {

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

            return e;
        })
Run Code Online (Sandbox Code Playgroud)

但是它没有深层复制内部项目所以我需要调整它:

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;
    }, {});
}
Run Code Online (Sandbox Code Playgroud)

这不太优雅,因为它需要知道传递了哪些对象.ES6中是否有一种方法可以使用扩展语法来深度复制对象?

Nik*_*rao 79

而是将其用于深层复制

var newObject = JSON.parse(JSON.stringify(oldObject))
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

  • 这仅在您不需要克隆功能时才有效.JSON将忽略所有函数,因此您不会在克隆中使用它们. (48认同)
  • 您使用JSON序列化的解决方案存在一些问题。这样,您将丢失任何在JSON中没有等效类型的Javascript属性,例如Function或Infinity。分配给undefined的任何属性都将被JSON.stringify忽略,从而使它们在克隆的对象上丢失。而且,某些对象会转换为字符串,例如Date,Set,Map等。 (5认同)
  • 除了函数之外,使用此方法还会出现undefined和null的问题 (4认同)
  • 您还会遇到任何用户定义类的问题,因为原型链未序列化。 (3认同)
  • 我做了一个可怕的噩梦,试图创建一个对象数组的真实副本 - 这些对象本质上是数据值,没有函数。如果这就是您所需要担心的,那么这种方法效果很好。 (2认同)

Fra*_*Tan 61

ES6没有内置这样的功能.我认为你有几个选择取决于你想做什么.

如果你真的想要深层复制:

  1. 使用图书馆.例如,lodash有一个cloneDeep方法.
  2. 实现自己的克隆功能.

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

但是,我想,如果你愿意改变一些事情,你可以节省一些工作.我假设您控制所有呼叫站点到您的功能.

  1. 指定传递给的所有回调mapCopy必须返回新对象,而不是改变现有对象.例如:

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

    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;
      }, {});
    }
    
    Run Code Online (Sandbox Code Playgroud)

从本质上讲,mapCopy是信任其来电者做正确的事情.这就是我说这假设您控制所有呼叫站点的原因.

  • Object.assign不会深度复制对象。请参阅https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign-Object.assign()复制属性值。“如果源值是对对象的引用,则它仅复制该引用值。” (2认同)

Abo*_*zlR 28

您可以使用structuredClone()您可以像下面这样

\n
const myOriginal = {\n  title: "Full Stack JavaScript Developer",\n  info: {\n    firstname: "Javascript",\n    surname: " Addicted",\n    age: 34\n  }\n};\nconst myDeepCopy = structuredClone(myOriginal);\n
Run Code Online (Sandbox Code Playgroud)\n

结构化克隆()

\n

您可以使用structuredClone()内置函数进行深度复制。\n结构化克隆解决了许多(尽管不是全部)缺点JSON.stringify()技术的许多(尽管不是全部)缺点。\n结构化克隆可以处理循环数据结构,\n支持许多内置数据类型,并且通常更稳健并且速度更快。

\n

然而,它仍然有一些限制,可能会让您措手不及:

\n

1-原型:如果您使用structuredClone()与类实例一起使用,\n您\xe2\x80\x99 将获得一个普通对象作为返回值,因为结构化克隆会丢弃对象\xe2\x80\x99s 原型链。

\n

2-功能:如果你的对象包含函数,它们将被悄悄丢弃。

\n

3-不可克隆:某些值不是结构化可克隆的,尤其是错误和 DOM 节点。它将导致 StructuredClone() 抛出异常。

\n
const myDeepCopy = structuredClone(myOriginal);\n
Run Code Online (Sandbox Code Playgroud)\n

JSON.stringify

\n

如果您只是想将对象深度复制到另一个对象,\n您需要做的就是JSON.stringify该对象并使用解析它JSON.parse随后使用它进行解析。\n这实际上将执行对象的深复制。

\n
let user1 = {\n  name: \'Javascript Addicted\',\n  age: 34,\n  university: {\n    name: \'Shiraz Bahonar University\'\n  }\n};\n\n\nlet user2 = JSON.parse(JSON.stringify(user1));\n\nuser2.name = \'Andy Madadian\';\nuser2.university.name = \'Kerman Bahonar University\'\n\nconsole.log(user2);\n// { name: \'Andy Madadian\', age: 33, university: { name: \'Kerman Bahonar University\' } }\n\nconsole.log(user1);\n// { name: \'Javascript Addicted\', age: 33, university: { name: \'Shiraz Bahonar University\' } }\n
Run Code Online (Sandbox Code Playgroud)\n

扩展运算符 / Object.assign()

\n

在 JavaScript 中使用对象扩展运算符创建浅拷贝的一种方法...或者Object.assign()如下所示:

\n
const myShallowCopySpread = {...myOriginal};\nconst myShallowCopyObjectAssign=Object.assign({},obj)\n
Run Code Online (Sandbox Code Playgroud)\n

表现

\n

当谈到性能时,创建者 Surma 指出,JSON.Parse()对于小物体来说,速度可能会更快一些。但是当您有一个大对象、复杂对象时\nstructuredClone()开始变得更快。

\n

浏览器支持非常棒,甚至还受Node.js支持支持。

\n


Min*_*uke 23

来自MDN

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

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

这是一个工作示例:

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
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
Run Code Online (Sandbox Code Playgroud)

  • arr6 也不适合我。在浏览器中 - chrome 65 (3认同)
  • arr6对我不起作用。在支持ES6的浏览器(chrome 59.0中,我得到** Uncaught SyntaxError:Unexpected token ... **,在支持ES7的节点8.9.3中,我得到** TypeError:undefined is not function at repl:1:22 ** (2认同)

Hec*_*Guo 9

我经常使用这个:

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;
        }, {})
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

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
Run Code Online (Sandbox Code Playgroud)

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