배열이 아닌 객체로 작동하는 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에는 이러한 기능이 내장되어 있지 않습니다. 하고 싶은 일에 따라 몇 가지 옵션이 있다고 생각합니다.
정말 깊은 복사를 원하는 경우 :
- 도서관을 이용하십시오. 예를 들어 lodash에는
cloneDeep
메서드가 있습니다. - 자신 만의 복제 기능을 구현하십시오.
특정 문제에 대한 대체 솔루션 (딥 카피 없음)
그러나 몇 가지를 기꺼이 바꾸고 싶다면 약간의 작업을 절약 할 수 있습니다. 나는 당신이 당신의 기능에 대한 모든 호출 사이트를 제어한다고 가정하고 있습니다.
-
전달 된 모든 콜백
mapCopy
이 기존 객체를 변경하는 대신 새 객체를 반환 하도록 지정 합니다. 예를 들면 :mapCopy(state, e => { if (e.id === action.id) { return Object.assign({}, e, { title: 'new item' }); } else { return e; } });
이를 사용
Object.assign
하여 새 개체를 만들고 해당 새 개체에e
대한 속성 을 설정 한 다음 새 개체에 새 제목을 설정합니다. 즉, 기존 개체를 변경하지 않고 필요할 때만 새 개체를 만듭니다. -
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;
}, {})
}
}
답변
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
사용 JSON.stringify
및 것이 JSON.parse
가장 좋은 방법입니다. 확산 연산자를 사용하면 json 객체에 다른 객체가 포함되어있을 때 효율적인 답을 얻을 수 없기 때문입니다. 수동으로 지정해야합니다.
답변
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
}