2016-07-16 223 views
26

Sto provando a creare un metodo di copia di una mappa profonda per il mio progetto Redux che funzionerà con gli oggetti piuttosto che con gli array. Ho letto che in Redux ogni stato non dovrebbe cambiare nulla negli stati precedenti.Copia profonda in ES6 utilizzando il simbolo di diffusione

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

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

    return output; 
    }, {}); 
} 

Funziona:

return mapCopy(state, e => { 

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

      return e; 
     }) 

Tuttavia esso elementi interni non copia completa quindi ho bisogno di modificarlo per:

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

Questo è meno elegante in quanto richiede di sapere che gli oggetti sono passati. Esiste un modo in ES6 per utilizzare il segno di diffusione per copiare in profondità un oggetto?

+0

Vedere http://stackoverflow.com/questions/27936772/deep-object-merging-in-es6-es7. –

+3

Questo è un problema XY. Non dovresti lavorare molto su proprietà profonde in redux. invece dovresti semplicemente creare un altro riduttore che lavori sulla sezione figlio della forma di stato e quindi usare 'combinaRiduttori 'per comporre i due (o più) insieme. Se usi tecniche di ridondanza idiomatica, il tuo problema di oggetti di clonazione profonda scompare. – naomik

risposta

19

Nessuna funzionalità è integrata in ES6. Penso che tu abbia un paio di opzioni a seconda di cosa vuoi fare.

Se davvero si vuole copia completa:

  1. utilizzare una libreria. Ad esempio, lodash ha un metodo cloneDeep.
  2. Implementa la tua funzione di clonazione.

Tuttavia, penso che se sei disposto a cambiare un paio di cose, puoi risparmiare un po 'di lavoro. Suppongo che tu controlli tutti i siti di chiamata alla tua funzione.

  1. specificare che tutte le richiamate passati a mapCopy devono restituire nuovi oggetti invece di mutare l'oggetto esistente. Per esempio:

    mapCopy(state, e => { 
        if (e.id === action.id) { 
        return Object.assign({}, e, { 
         title: 'new item' 
        }); 
        } else { 
        return e; 
        } 
    }); 
    

    Questo fa uso di Object.assign per creare un nuovo oggetto, imposta le proprietà di e su quel nuovo oggetto, quindi imposta un nuovo titolo su quel nuovo oggetto. Ciò significa che non muti mai oggetti esistenti e ne crei di nuovi quando necessario.

  2. mapCopy può essere davvero semplice ora:

    export const mapCopy = (object, callback) => { 
        return Object.keys(object).reduce(function (output, key) { 
        output[key] = callback.call(this, object[key]); 
        return output; 
        }, {}); 
    } 
    

In sostanza, mapCopy si fida dei suoi chiamanti di fare la cosa giusta. Questo è il motivo per cui ho detto che questo presuppone che tu controlli tutti i siti di chiamata.

4

Da NDP

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it's the same with Object.assign() and spread syntax).

personalmente, suggeriamo di utilizzare Lodash funzione cloneDeep multi-livello di oggetto/array clonazione.

Ecco un esempio di lavoro:

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>

+0

arr6 non funziona per me. Nel browser (Chrome 59.0 che supporta ES6 ottengo ** Uncaught SyntaxError: Token inatteso ... ** e nel nodo 8.9.3 che supporta ES7 Ottengo ** TypeError: undefined non è una funzione che repl: 1: 22 ** –

+0

@ AchiEven-dar non sire il motivo per cui si è verificato un errore. È possibile eseguire questo codice direttamente in stackoverflow premendo il pulsante blu "Esegui snippet di codice" e dovrebbe essere eseguito correttamente. –

8

utilizzare invece questo per copia completa

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