Yayılma sözdizimini kullanarak ES6'da derin kopya


106

Redux projem için diziler yerine nesnelerle çalışacak bir derin kopya haritası yöntemi oluşturmaya çalışıyorum. Redux'te her durumun önceki durumlarda hiçbir şeyi değiştirmemesi gerektiğini okudum.

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

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

    return output;
    }, {});
}

İşe yarıyor:

    return mapCopy(state, e => {

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

            return e;
        })

Ancak, iç öğeleri derinlemesine kopyalamıyor, bu yüzden şu şekilde ayarlamam gerekiyor:

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

Bu, hangi nesnelerin geçirildiğini bilmek gerektirdiğinden daha az zariftir. ES6'da bir nesneyi derinlemesine kopyalamak için yayılma sözdizimini kullanmanın bir yolu var mı?



8
Bu bir XY sorunudur. Redux'teki derin özellikler üzerinde fazla çalışmanıza gerek kalmaz. bunun yerine, durum şeklinin alt diliminde çalışan başka bir indirgeyici oluşturmalı ve ardından combineReducersikisini (veya daha fazlasını) birlikte oluşturmak için kullanmalısınız . Deyimsel tekrarlama tekniklerini kullanırsanız, nesneleri derinlemesine klonlama probleminiz ortadan kalkar.
Teşekkürler

Yanıtlar:


73

ES6'da böyle bir işlevsellik yerleşik değildir. Ne yapmak istediğine bağlı olarak birkaç seçeneğin olduğunu düşünüyorum.

Gerçekten derin kopyalamak istiyorsanız:

  1. Bir kitaplık kullanın. Örneğin lodash'ın bir cloneDeepyöntemi var.
  2. Kendi klonlama işlevinizi uygulayın.

Özel Probleminize Alternatif Çözüm (Derin Kopya Yok)

Bununla birlikte, birkaç şeyi değiştirmeye istekliysen, kendine biraz işten tasarruf edebileceğini düşünüyorum. İşlevinize tüm çağrı sitelerini kontrol ettiğinizi varsayıyorum.

  1. mapCopyVar olan nesneyi değiştirmek yerine, iletilen tüm geri aramaların yeni nesneler döndürmesi gerektiğini belirtin . Örneğin:

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

    Bu, Object.assignyeni bir nesne oluşturmak için kullanır, ebu yeni nesnenin özelliklerini ayarlar ve ardından bu yeni nesneye yeni bir başlık ayarlar. Bu, mevcut nesneleri asla değiştirmeyeceğiniz ve yalnızca gerektiğinde yenilerini oluşturmayacağınız anlamına gelir.

  2. mapCopy şimdi gerçekten basit olabilir:

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

Esasen, mapCopyarayanların doğru şeyi yapacağına güveniyor. Bu yüzden, bunun tüm arama sitelerini kontrol ettiğinizi varsaydığını söyledim.


4
Object.assign, nesneleri derinlemesine kopyalamaz. Developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… bakın - Object.assign () özellik değerlerini kopyalar. "Kaynak değeri bir nesneye başvuruysa, yalnızca bu referans değerini kopyalar."
Greg Somers

Sağ. Bu etmeyen bir alternatif çözüm değil , kapsamlı kopyalama içerir. Cevabımı bu konuda daha açık olacak şekilde güncelleyeceğim.
Frank Tan

107

Bunun yerine bunu derin kopya için kullanın

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


66
Bu, yalnızca işlevleri klonlamanız gerekmediğinde çalışır. JSON tüm işlevleri yok sayacak, böylece klonda bunlara sahip olmayacaksınız.
Noland

7
İşlevlerin yanı sıra, bu yöntemi kullanarak tanımlanmamış ve boş ile ilgili sorunlar
yaşayacaksınız

3
Prototip zincirleri serileştirilmediğinden, kullanıcı tanımlı sınıflarla da sorun yaşarsınız.
Patrick Roberts

9
JSON serileştirme kullanan çözümünüzde bazı sorunlar var. Bunu yaptığınızda, Function veya Infinity gibi JSON'da eşdeğer türü olmayan tüm Javascript özelliklerini kaybedersiniz. Undefined olarak atanan herhangi bir özellik JSON.stringify tarafından yok sayılacak ve klonlanan nesnede gözden kaçmasına neden olacaktır. Ayrıca, Tarih, Küme, Harita ve diğerleri gibi bazı nesneler dizelere dönüştürülür.
Jonathan Brizio

2
Bir dizi nesnenin gerçek bir kopyasını yaratmaya çalışırken korkunç bir kabus görüyordum - esasen veri değerleri olan, işlevsiz nesneler. Endişelenmen gereken tek şey buysa, bu yaklaşım çok güzel çalışıyor.
Charlie

31

MDN'den

Not: Yayılma sözdizimi, bir dizi kopyalanırken etkili bir şekilde bir seviye derine iner. Bu nedenle, aşağıdaki örnekte gösterildiği gibi çok boyutlu dizileri kopyalamak için uygun olmayabilir (Object.assign () ve spread sözdizimi ile aynıdır).

Kişisel olarak, çok seviyeli nesne / dizi klonlama için Lodash'ın cloneDeep işlevini kullanmanızı öneririm .

İşte çalışan bir örnek:

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>


4
arr6 benim için çalışmıyor. Tarayıcıda (ES6'yı destekleyen krom 59.0 Yakalanmamış Sözdizimi Hatası : Beklenmeyen belirteç alıyorum ... ve ES7'yi destekleyen düğüm 8.9.3'te TypeError: tanımsız bir işlev değil repl: 1: 22
Achi Even-dar

@ AchiEven-dar değil neden bir hata aldınız. Bu kodu, mavi düğmeye basarak doğrudan stackoverflow'da Run code snippetçalıştırabilirsiniz ve düzgün çalışması gerekir.
Mina Luke

3
arr6 benim için de çalışmıyor. In browser - chrome 65
yehonatan yehezkel

19

Bunu sık sık kullanıyorum:

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

3
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

Kullanılması JSON.stringifyve JSON.parseen iyi yoldur. Çünkü yayılma operatörünü kullanarak json nesnesi içinde başka bir nesne içerdiğinde etkili yanıtı alamayacağız. bunu manuel olarak belirtmemiz gerekiyor.


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

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

Açıklama arayanlar için yorumlar koddadır.
Wookies-Will-Code

1
const cloneData = (dataArray) => {
    newData= []
    dataArray.forEach((value) => {
        newData.push({...value})
    })
    return newData
}
  • a = [{ad: "siva"}, {ad: "siva1"}];
  • b = myCopy (a)
  • b === a // yanlış`

1

Geçen gün, yinelemeli bağlantılar da içerebilen karmaşık yapıları derinlemesine kopyalamak için bir yol bulmaya çalışarak bu cevaplara kendim ulaştım. Daha önce önerilen hiçbir şeyden memnun olmadığım için bu tekerleği kendim uyguladım. Ve oldukça iyi çalışıyor. Umarım birine yardımcı olur.

Örnek kullanım:

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

TheClone = OriginalStruct.deep_copy();

Nasıl kullanılacağına dair canlı örnekler için lütfen https://github.com/latitov/JS_DeepCopy adresine bakın ve ayrıca deep_print () de var.

Çabuk ihtiyacınız varsa, işte deep_copy () işlevinin kaynağı:

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

Şerefe @!


1

İşte tüm ilkel, dizi, nesne, işlev veri türlerini işleyen deepClone işlevi

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


1

İşte benim derin kopya algoritmam.

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

Ayrıca olmadığını kontrol etmek gerek typeof (null) da 'nesne' dönmek gibi 'obj [prop] == null adlı!'
Pramod Mali
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.