复制不可变数组或对象

时间:2018-10-19 12:55:40

标签: javascript node.js ecmascript-6

我在javascript项目中有几个函数,它们接受一个对象作为参数,并期望通过一些更改返回该对象的副本。我试图使这些函数不可变,但是参数可以是对象或数组,因此我不能只使用Object.assign({}, original){...original}

相反,我提出了两个选择:

let doSomething = function doSomething(original) {
    let ret = Object.assign(Array.isArray(original) ? [] : {}, original);

    //OR
    let ret = Array.isArray(original) ? [...original] : {...original};

    //OR
    let ret = Object.assign(new (original.constructor), original);

    //OR
    let ret;

    if(Array.isArray(original))
        ret = original.slice();
    else
        ret = Object.assign({}, original);

    //make changes

    return ret;
}

在这四种方法中,我偏爱第三种方法,只是因为它支持任何类型的对象(不是我打算使用除Objects或Arrays之外的任何方法),但是它感觉很“聪明”

在四种方法中,哪种方法最易于阅读?还是有另一种首选的方式可以复制任何类型的对象?

2 个答案:

答案 0 :(得分:2)

选项#1,#2和#4的功能完全相同。至于可读性,我的投票是第二名-最短和最清晰。

选项3还会重新创建在这种情况下不需要的构造函数/原型链。它将允许运行copiedObj instanceof OriginalObjectClass之类的检查。但是,一旦构造函数期望一些参数(并且不会明显得到它们),它也可能会失败。因此,在您的情况下,这看起来很危险,但却无济于事。

我也完全同意D Lowther,最好从递归函数中提取克隆本身。

但是我想您不仅要克隆某个对象,而且还要递归处理克隆的嵌套属性。这样,您还需要检查此对象或数组,对吗?因此,与其分别克隆数组然后再克隆其成员,不如使用.map在一行中完成,就更容易了:

function cloneRecursively(originalItem) {
    let clonedItem = {...originalItem};
    clonedItem.children = (clonedItem.children || []).map(cloneRevursively);
    return clonedItem;
}

答案 1 :(得分:1)

您可以使用constructor方法代替new (original.constructor)

由于您的参数可以是objectarray,因此可以这样写:

let ret = Object.assign(original.constructor(), original)

这样调用,将返回一个空的objectarray

请注意,如果您的参数不是普通的JS object,则使用constructor可能会失败或返回意外内容