Я пытаюсь создать метод карты глубокого копирования для моего проекта 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 использовать синтаксис распространения для глубокого копирования объекта?
combineReducers
для объединения двух (или более) вместе. Если вы используете идиоматические методы редукции, ваша проблема глубокого клонирования объектов исчезнет.Ответы:
В ES6 нет такой функции. Я думаю, у вас есть несколько вариантов в зависимости от того, что вы хотите сделать.
Если вы действительно хотите глубоко скопировать:
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
Лично я предлагаю использовать функцию 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>
источник
Run code snippet
и он должен работать правильно.Я часто использую это:
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 }
источник
const cloneData = (dataArray) => { newData= [] dataArray.forEach((value) => { newData.push({...value}) }) return newData }
источник
Я сам натолкнулся на эти ответы в прошлый день, пытаясь найти способ глубокого копирования сложных структур, которые могут включать рекурсивные ссылки. Поскольку меня не устраивало что-либо из предложенных ранее, я сам реализовал это колесо. И работает неплохо. Надеюсь, это кому-то поможет.
Пример использования:
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; } }
Ура @!
источник
Вот функция deepClone, которая обрабатывает все типы данных примитивов, массивов, объектов и функций.
function deepClone(obj){ if(Array.isArray(obj)){ var arr = []; for (var i = 0; i < obj.length; i++) { arr[i] = deepClone(obj[i]); } return arr; } if(typeof(obj) == "object"){ var cloned = {}; for(let key in obj){ cloned[key] = deepClone(obj[key]) } return cloned; } return obj; } console.log( deepClone(1) ) console.log( deepClone('abc') ) console.log( deepClone([1,2]) ) console.log( deepClone({a: 'abc', b: 'def'}) ) console.log( deepClone({ a: 'a', num: 123, func: function(){'hello'}, arr: [[1,2,3,[4,5]], 'def'], obj: { one: { two: { three: 3 } } } }) )
источник
Вот мой алгоритм глубокого копирования.
const DeepClone = (obj) => { if(obj===null||typeof(obj)!=='object')return null; let newObj = { ...obj }; for (let prop in obj) { if ( typeof obj[prop] === "object" || typeof obj[prop] === "function" ) { newObj[prop] = DeepClone(obj[prop]); } } return newObj; };
источник