我可以使用Destructuring创建深层副本吗?

时间:2019-07-23 14:17:54

标签: javascript ecmascript-6 destructuring

基本上,我想得到一个的深层副本,该副本不会使用解构来更改我的主要对象。

let a = {
    name: 'lala', 
  testArray: [1,2,3], 
  object: {
    name: 'object', 
    array: [4,5,6]
  }
};

const {name, testArray, object} =  a;

object.array = [...object.array, 0];
console.log(a.object.array);

let b = {
  object2: {
    name: 'object', 
    array: [4,5,6]
  }
};

const object2 =  {...b.object2};
object2.array = [...object2.array, 0];

console.log(b.object2.array);

我做了一个jsfiddle(为了更容易重现),提供了我编写的代码。

https://jsfiddle.net/5z71Lbja/

问题在于,当我使用第一种方法(解构)更改“子”对象时,主要对象的数组也会更改。第二种方法可以正常工作,但是我很好奇我是否可以使用解构获得相同的结果。

3 个答案:

答案 0 :(得分:1)

不能通过解构来创建新对象,不可以。您只能选择源中存在的值,不能对其进行转换。 (您可以更改使用的变量名,但不能转换值。)我经常想要,但您不能(至少目前不是)。

您可以通过多种方式进行跳转,但实际上最简单的方法是分别制作数组的浅表副本。

一个简单的例子:

const obj = {
  foo: "bar",
  array: [1, 2, 3]
};

const {foo} = obj;
const array = obj.array.slice(); // or: = [...obj.array];

obj.array[0] = "one";
console.log(obj.array[0]); // "one"
console.log(array[0]);     // 1

答案 1 :(得分:0)

这不可能直接实现。

let { object } = a;
object = {...object}; // Object.assign({}, object); <<

object.array = [0, ...object.array, 0];

console.log(object.array); // [0, 4, 5, 6, 0]
console.log(a.object.array); // [4, 5, 6]

答案 2 :(得分:0)

答案

您可以通过功能或直接使用Proxy对象来实现此目的。


在销毁期间使用函数

Proxy对象采用target对象和handler

handler允许您设置某些条件,例如getset,这些条件可以更改将数据返回给用户的方式。我们将为此使用get

在下面的处理程序中,我们更改了get功能。我们检查target[prop]是否返回ArrayObject。如果是这样,我们将在内存中创建一个副本,并返回该副本而不是引用。如果它不是ArrayObject,我们只需返回原始值(字符串,数字等)

let copyHandler = {
  get: function( target, prop, receiver ) {
    let value = target[ prop ];
    if ( Array.isArray( value ) ) return value.slice( 0 );
    if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
    return value;
  }
}, getCopy = obj => new Proxy(obj, copyHandler);

利用getCopy函数作为我们的破坏中间人,我们可以确保我们所有的值都返回新的引用:

const {
  name,
  testArray,
  object
} = getCopy(a);


object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]

示例:

let copyHandler = {
  get: function( target, prop, receiver ) {
    let value = target[ prop ];
    if ( Array.isArray( value ) ) return value.slice( 0 );
    if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
    return value;
  }
}, getCopy = obj => new Proxy(obj, copyHandler);



let a = {
  name: 'lala',
  testArray: [ 1, 2, 3 ],
  object: {
    name: 'object',
    array: [ 4, 5, 6 ]
  }
};

const {
  name,
  testArray,
  object
} = getCopy(a);



object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]


或者,我们可以直接在“声明/初始化/接收”上这样做:

从这个意义上说,直接意味着我们可以将Object设置为仅在非结构化声明期间返回副本。

我们通过使用Proxy和中间人函数来完成上述操作。

注意:中间人功能不是必需的,但它有助于使事情井井有条。

let destructHandler = {
  get: function( target, prop, receiver ) {
    if(!this.received) this.received = new Set();

    let value = target[ prop ];
    if(this.received.has(prop)) return value;

    this.received.add(prop);

    if ( Array.isArray( value ) ) return value.slice( 0 );
    if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
    return value;
  }, destructable = obj => new Proxy(obj, destructHandler);

这里的区别在于我们的get处理程序使用Set来确定某个属性是否已经被抓过一次。

在首次请求引用属性(ArrayObject)时,它将返回副本。它仍将照常返回任何原始值。

这意味着在声明/初始化/接收对象后,您可以应用destructable代理,然后立即使用销毁从该对象中提取副本。

初始化示例代码:

let a = destructable({
  name: 'lala',
  testArray: [ 1, 2, 3 ],
  object: {
    name: 'object',
    array: [ 4, 5, 6 ]
  }
});

解构示例:

const {
  name,
  testArray,
  object
} = a;

object.array = [...object.array, 0];
console.log(a.object.array); // [4,5,6]
console.log(object.array); // [4,5,6,0]

示例:

let destructHandler = {
  get: function( target, prop, receiver ) {
    if(!this.received) this.received = new Set();
    
    let value = target[ prop ];
    if(this.received.has(prop)) return value;
    
    this.received.add(prop);
    
    if ( Array.isArray( value ) ) return value.slice( 0 );
    if ( typeof value === "object" && value.constructor.name === "Object" ) return Object.assign( {}, value );
    return value;
  }
}, destructable = obj => new Proxy(obj, destructHandler);


let a = destructable({
  name: 'lala',
  testArray: [ 1, 2, 3 ],
  object: {
    name: 'object',
    array: [ 4, 5, 6 ]
  }
});

const {
  name,
  testArray,
  object
} = a;
    object.array = [...object.array, 0];
    console.log(object.array); // [4,5,6,0]
    console.log(a.object.array); // [4,5,6]

希望这会有所帮助!祝您编码愉快!