developer tip

확산 구문을 사용하는 ES6의 전체 복사

copycodes 2020. 10. 16. 07:32
반응형

확산 구문을 사용하는 ES6의 전체 복사


배열이 아닌 객체로 작동하는 Redux 프로젝트에 대한 깊은 복사 맵 방법을 만들려고합니다. Redux에서 각 상태는 이전 상태에서 아무것도 변경해서는 안된다고 읽었습니다.

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

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

    return output;
    }, {});
}

효과가있다:

    return mapCopy(state, e => {

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

            return e;
        })

그러나 내부 항목을 딥 복사하지 않으므로 다음과 같이 조정해야합니다.

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;
    }, {});
}

어떤 객체가 전달되는지 알아야하기 때문에 덜 우아합니다. ES6에서 스프레드 구문을 사용하여 객체를 딥 복사하는 방법이 있습니까?


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;
      }
    });
    

    이를 사용 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;
      }, {});
    }
    

기본적 mapCopy으로 발신자가 올바른 일을하도록 신뢰하는 것입니다. 이것이 내가 이것이 모든 통화 사이트를 제어한다고 가정 한 이유입니다.


대신 딥 카피에 이것을 사용하십시오.

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);


MDN에서

참고 : 스프레드 구문은 배열을 복사하는 동안 효과적으로 한 수준 깊이가됩니다. 따라서 다음 예제와 같이 다차원 배열을 복사하는 데 적합하지 않을 수 있습니다 (Object.assign () 및 스프레드 구문과 동일 함).

개인적 으로 다단계 개체 / 어레이 복제를 위해 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
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>


나는 이것을 자주 사용합니다 :

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;
        }, {})
    }
}

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;
    }
}

// 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
}

나는 지난날이 답변에 착수하여 재귀 링크를 포함 할 수있는 복잡한 구조를 딥 복사하는 방법을 찾으려고 노력했습니다. 이전에 제안 된 내용에 만족하지 않았기 때문에이 바퀴를 직접 구현했습니다. 그리고 그것은 아주 잘 작동합니다. 누군가에게 도움이되기를 바랍니다.

사용 예 :

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

TheClone = OriginalStruct.deep_copy();

사용 방법에 대한 실제 예제 https://github.com/latitov/JS_DeepCopy참조하십시오. 또한 deep_print ()도 있습니다.

빨리 필요한 경우 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;
    }
}

건배@!


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

참고 URL : https://stackoverflow.com/questions/38416020/deep-copy-in-es6-using-the-spread-syntax

반응형