Diziyi değere göre kopyala


1745

JavaScript'teki bir diziyi başka bir diziye kopyalarken:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

Bunun yeni, bağımsız bir dizi yerine arr2aynı diziyi ifade ettiğini fark ettim arr1. İki bağımsız dizi elde etmek için diziyi nasıl kopyalayabilirim?


3
Şu anda Chrome 53 ve Firefox 48'de olduğu gibi slice, spliceoperasyonlar ve yeni spread operatörü için harika bir performansa Array.fromsahibiz ve çok daha yavaş bir uygulamaya sahibiz. Perfjs.fnfo
Pencroff

jsben.ch/#/wQ9RU <= Bu temel ölçü bir diziyi kopyalamanın farklı yolları hakkında genel bir bakış sunuyor
EscapeNetscape


Bu dizide (ilkel dizeleri içeren bir) için kullanabileceğiniz var arr2 = arr1.splice();derin kopyasına, ancak bu teknik sizin dizideki elemanları değişmez yapılar (yani içerecektir değil iş eğer []ya {}) ya da prototip nesneleri (yani function () {}, newvs.). Diğer çözümler için aşağıdaki cevabıma bakın.
tfmontague

16
2017, bu nedenle ES6 özelliklerini kullanmayı düşünebilirsiniz: let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Hinrich

Yanıtlar:


2701

Bunu kullan:

var newArray = oldArray.slice();

Temel olarak, slice()işlem diziyi klonlar ve yeni bir diziye başvuru döndürür.

Ayrıca şunu da unutmayın:

Referanslar, dizeler ve sayılar için (gerçek nesne değil), slice()nesne referanslarını yeni diziye kopyalar. Hem orijinal hem de yeni dizi aynı nesneyi belirtir. Başvurulan bir nesne değişirse, değişiklikler hem yeni hem de orijinal diziler tarafından görülebilir.

Dize ve sayı gibi temel öğeler değiştirilemez, bu nedenle dize veya sayıdaki değişiklikler imkansızdır.


9
Performansla ilgili olarak aşağıdaki jsPerf testleri aslında var arr2 = arr1.slice () öğesinin var arr2 = arr1.concat () kadar hızlı olduğunu göstermektedir; JSPerf: jsperf.com/copy-array-slice-vs-concat/5 ve jsperf.com/copy-simple-array . Jsperf.com/array-copy/5 türünün sonucu beni test kodunun geçerli olup olmadığını merak ettiğim noktaya şaşırttı.
Cohen

94
Bu zaten bir ton upvotes almış olsa da, başka bir şeyi hak ediyor çünkü JS'de ne yazık ki nadir görülen referansları düzgün bir şekilde anlatıyor.
Wayne

34
Sadece okunabilirlik için bir kütüphanenin tamamını ekleyebilir misiniz? Gerçekten mi? Okunabilirlikle ilgileniyor olsaydım sadece bir yorum eklerdim. Bkz. Var newArray = oldArray.slice (); // Clone oldArray to newArray
dudewad

12
@ GáborImre Anlıyorum. Ancak bence bir kütüphanenin tamamını ekleyerek belirli bir mühendislik problemini cevaplamak yardımcı olmaz, tasarım şişmesi. Geliştiricilerin bunu çok yaptıklarını görüyorum ve sonra tek bir işlev yazmak zorunda kalmanın yerini alan bir çerçeve içeren bir proje ile sonuçlanıyorsunuz. Yine de benim MO'm.
dudewad

5
Ders: karıştırmayın Do .slice()ile .splice(), size boş bir dizi veren. Büyük fark.
crazypeter

531

Javascript'te derin kopyalama teknikleri bir dizideki öğelere bağlıdır. Oradan başlayalım.

Üç tür eleman

Öğeler şunlar olabilir: değişmez değerler, değişmez yapılar veya prototipler.

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}`

Bu unsurlardan üç tür dizi oluşturabiliriz.

// 1) Array of literal-values (boolean, number, string) 
const type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
const type2 = [[], {}];

// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];

Derin kopyalama teknikleri üç dizi türüne bağlıdır

Dizideki eleman türlerine dayanarak, derin kopyalamak için çeşitli teknikler kullanabiliriz.

Öğe türlerine göre Javascript derin kopyalama teknikleri

  • Değişmez değerlerinin (Tip1) dizisi , , , ve teknikler sadece edebi değerler (boolean, sayı ve string) ile derin kopya diziler için kullanılabilir; Spread operatörünün en iyi performansı gösterdiği yer ( https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat ).
    [...myArray]myArray.splice(0)myArray.slice()myArray.concat()[...myArray]

  • Değişmez değerlerinin (Tip1) ve edebi-yapıları (tip2) dizisi tekniği derin kopya değişmez değerler (boolean, sayı, dize) ve değişmez yapıların (dizi, nesne) için kullanılır, ancak nesneleri prototipini değil edilebilir.
    JSON.parse(JSON.stringify(myArray))

  • Tüm diziler (tip1, tip2, tip3)
    jQuery $.extend(myArray)tekniği tüm dizi türlerini derin kopyalamak için kullanılabilir. Gibi Kütüphaneler alt çizgi ve Lo-tire için teklif benzer derin kopya fonksiyonları jQuery $.extend() , henüz alt performansa sahip. Daha şaşırtıcı bir şekilde, http://jsperf.com/js-deep-copy/15 tekniğinden $.extend()daha yüksek performansa sahiptir . Ve üçüncü taraf kitaplıklarından (jQuery gibi) uzaklaşan geliştiriciler için aşağıdaki özel işlevi kullanabilirsiniz; $ .extend'den daha yüksek performansa sahip olan ve tüm dizileri derin kopyalayan.JSON.parse(JSON.stringify(myArray))

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

Soruyu cevaplamak için ...

Soru

var arr1 = ['a','b','c'];
var arr2 = arr1;

Arr2'nin yeni, bağımsız bir dizi yerine arr1 ile aynı diziyi ifade ettiğini fark ettim. İki bağımsız dizi elde etmek için diziyi nasıl kopyalayabilirim?

Cevap

Çünkü arr1değişmez değerler (boolean, sayı veya string) bir dizidir, sen yayılmış operatörü yukarıda tartışılan herhangi bir derin kopya tekniği kullanabilir ...en yüksek performansa sahiptir.

// Highest performance for deep copying literal values
arr2 = [...arr1];

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above

1
Bu yaklaşımların çoğu işe yaramıyor. Atama işlecini kullanmak, öğesinin orijinal değişmez değerini yeniden atamanız gerektiği anlamına gelir arr1. Durum böyle olacağı çok nadirdir. Kullanmak spliceyok arr1, bu yüzden bu bir kopya değil. Dizideki JSONdeğerlerden herhangi biri İşlevse veya prototip (a gibi Date) varsa, kullanım başarısız olur .
Dancrumb

Ek yeri kullanmak kısmi bir çözümdür. JSON'dan çok daha fazla durumda başarısız olacaktır. Splice, değerleri taşıdığında dizelerin ve sayıların derin bir kopyasını oluşturur - asla bir kopya döndürmediğini söylemedi.
tfmontague

1
Neden ekleme (0)? Dilim () olmamalı mı? Ben splice yaptığı orijinal dizi değiştirmek değil sanırım. @JamesMontagne
15'te yardım

2
ekleme orijinal dizideki öğelere (sığ kopya) işaretçiler oluşturur. splice (0), dizideki sayı veya dize olan öğeler için yeni bellek (derin kopya) tahsis eder ve diğer tüm öğe türleri için (sığ kopya) işaretçiler oluşturur. Ekleme fonksiyon yöntemine sıfır başlangıç ​​değeri ileterek, orijinal diziden herhangi bir öğeyi birleştirmez ve bu nedenle değiştirmez.
tfmontague

1
Aslında, sadece bir dizi dizi vardır: "somethings" dizisi. [0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]Başka bir dizi arasında hiçbir fark yoktur .
wizzwizz4

189

...Dizileri kopyalamak için dizi formalarını kullanabilirsiniz .

const itemsCopy = [...items];

Ayrıca mevcut dizinin bir parçası olan yeni bir dizi oluşturmak istiyorsanız:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

Dizi yayılımları artık tüm büyük tarayıcılarda desteklenmektedir, ancak daha eski desteğe ihtiyacınız varsa daktilo veya babel kullanın ve ES5 ile derleyin.

Formalar hakkında daha fazla bilgi


1
Bu derin kopya için işe yaramaz. JavaScript'te bir Diziyi Derin Klonlayın .
SNag

151

JQuery gerekmez ... Çalışma Örneği

var arr2 = arr1.slice()

Bu, diziyi başlangıç ​​konumundan 0dizinin sonuna kadar kopyalar.

İlkel türler (dize, sayı vb.) İçin beklendiği gibi çalışacağını ve referans türleri için beklenen davranışı açıklayacağını belirtmek önemlidir ...

Bir dizi Referans türünüz varsa, diyelim Object. Dizi olacaktır kopyalanacak ancak dizilerin her ikisi de aynı başvurular içerir Object's. Bu durumda, dizi gerçekten kopyalanmış olsa bile dizi referansla kopyalanmış gibi görünür .


12
Hayır, bu derin bir kopya olmayacaktı.
jondavidjohn

Bunu dene; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
Bu cevap ile kabul edilen cevap arasındaki fark nedir?
Isaac Pak

verdiğiniz örnek için konsolda hata alıyorum "TypeError: window.addEvent bir işlev değil"
Ravi Sharma

72

Bir alternatif sliceIS concat2 yolla kullanılabilir. Bunlardan ilki, amaçlanan davranış çok açık olduğu için belki de daha okunabilir:

var array2 = [].concat(array1);

İkinci yöntem:

var array2 = array1.concat();

Cohen (yorumlarda) bu ikinci yöntemin daha iyi performansa sahip olduğuna dikkat çekti .

Bunun çalışma concatyöntemi , yöntemin çağrıldığı nesnede bulunan öğelerden, ardından argüman olarak iletilen herhangi bir dizinin öğelerinden oluşan yeni bir dizi oluşturmasıdır. Dolayısıyla, hiçbir argüman iletilmediğinde, diziyi kopyalar.

Lee Penkman, ayrıca yaptıkları yorumlarda, bir şans varsa işaret array1olduğunu undefinedaşağıdaki gibi boş bir dizi dönebilirsiniz:

var array2 = [].concat(array1 || []);

Veya, ikinci yöntem için:

var array2 = (array1 || []).concat();

Ayrıca ile bunu unutmayın slice: var array2 = (array1 || []).slice();.


31
Aslında şunları da yapabilirsiniz: var array2 = array1.concat (); Performans konusunda çok daha hızlı. (JSPerf: jsperf.com/copy-simple-array ve jsperf.com/copy-array-slice-vs-concat/5
Cohen

5
Eğer dizi1 bir dizi değilse o zaman örneğin tanımsız alırsanız [].concat(array1)döneceğini [array1]belirtmek gerekir [undefined]. Bazen yapıyorumvar array2 = [].concat(array1 || []);
lee penkman

60

Birçok yaklaşımı denedikten sonra böyle yaptım:

var newArray = JSON.parse(JSON.stringify(orgArray));

Bu, ilkiyle ilgili olmayan yeni bir derin kopya oluşturur (sığ bir kopya değil).

Ayrıca bu açıkça olayları ve işlevleri klonlamayacaktır, ancak bunu bir satırda yapabileceğiniz iyi şey ve her türlü nesne için kullanılabilir (diziler, dizeler, sayılar, nesneler ...)


4
Bu en iyisi. Aynı yöntemi uzun zaman önce kullanıyorum ve eski okul özyinelemeli döngülerinde artık bir anlam olmadığını düşünüyorum
Vladimir Kharlampidi

1
Bu seçeneğin grafik benzeri yapıları iyi işlemediğini unutmayın: döngülerin varlığında çökmeler ve paylaşılan referansları korumaz.
Ruben

1
Bu aynı zamanda Datebir prototipi olan herhangi bir şey için ya da gerçekten de başarısız olur . Ayrıca, undefineds'ye dönüştürülür null.
Dancrumb

7
Kimse hem CPU'da hem de metinde serileştirme ve daha sonra bir nesneye ayrıştırma belleğindeki brüt verimsizliği yorumlayacak kadar cesur değil mi?
Lawrence Dol

3
Bu çözüm işe yarayan tek çözümdür. Slice () kullanmak gerçekten sahte bir çözümdür.

20

Bahsedilen yöntemlerden bazıları, sayı veya dize gibi basit veri türleriyle çalışırken iyi çalışır, ancak dizi başka nesneler içerdiğinde bu yöntemler başarısız olur. Herhangi bir nesneyi bir diziden diğerine geçirmeye çalıştığımızda, nesne olarak değil referans olarak geçirilir.

JavaScript dosyanıza aşağıdaki kodu ekleyin:

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

Ve sadece

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

Çalışacak.


2
Bu kodu sayfamıza eklediğimde bu hatayı alıyorum 'Yakalanmamış RangeError: Maksimum çağrı yığını boyutu aşıldı'
sawe

1
Özür dilerim, arr1 bildirilmezse bu hata kromda oluşur. bu yüzden yukarıdaki kodu kopyalayıp yapıştırdım ve dizi arr1 bildirirseniz, ancak hatayı alıyorum, sonra hatayı almıyorum. Eğer arr1'i arr2'nin hemen üzerinde ilan ederek cevabı geliştirebilirsiniz, görüyorum ki arr1'i beyan etmek zorunda olduğumuzu fark etmeyen 'kısmen' biziz var (kısmen cevabınızı değerlendirirken, acelem vardı ve 'sadece işe
yarayan

.slice()Dizinizde
Jason

7
@Jason ama nesneler hala aynı nesneyi gösteriyor, böylece birini değiştirmek diğerini değiştirecek. jsfiddle.net/k525g/1
Samuel

Mükemmel kod. Bir soru var, ben aslında bu gibi bir dizi kopyalamaya çalıştı var arr1 = yeni Array () ve sonra var arr2 = arr1; Eğer arr2'de bir şeyi değiştirirsem değişiklik arr1'de de olur. Ancak, yaptığınız klon prototipini kullanırsam, bu dizinin tamamen yeni bir örneğini oluşturur veya başka bir deyişle kopyalar. Peki bu bir tarayıcı sorunu mu? veya javascript varsayılan olarak biri değişken arr2 = arr1 yaptığında ve tamsayı değişkenlerinde neden olmadığında iki değişkeni işaretçi kullanarak diğerine işaret ederek ayarlar. bkz. jsfiddle.net/themhz/HbhtA
themhz


17

Şahsen Array.from'un daha okunabilir bir çözüm olduğunu düşünüyorum . Bu arada, tarayıcı desteğinden sakının.

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);

1
Evet, bu çok okunabilir. .slice()Çözelti tamamen unintuitive olduğu. Bunun için teşekkürler.
Banago

15

Önemli!

Buradaki cevapların çoğu belirli durumlar için geçerlidir .

Derin / iç içe nesneler ve sahne kullanımı ile ilgilenmiyorsanız ( ES6 ):

let clonedArray = [...array]

ancak derin klon yapmak istiyorsanız bunun yerine şunu kullanın:

let cloneArray = JSON.parse(JSON.stringify(array))


Lodash kullanıcıları için:

let clonedArray = _.clone(array) belgeleme

ve

let clonedArray = _.cloneDeep(array) belgeleme



9

Array.slice () çözümüne ekleme ; çok boyutlu dizi alt dizileriniz varsa referanslarla kopyalanacağını unutmayın. Yapabileceğiniz her bir alt diziyi ayrı ayrı döngü ve dilim () yapmaktır

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

aynı şeyler nesne dizisine gider, referans olarak kopyalanır, bunları manuel olarak kopyalamanız gerekir


Bu cevap, sayfanın üst kısmına yakın bir yeri hak ediyor! Çok boyutlu alt dizilerle çalışıyordum ve iç dizilerin neden val tarafından değil ref ile her zaman kopyalandığını takip edemedim. Bu basit mantık sorunumu çözdü. Mümkünse +100 veririm!
Mac

8

İlkel değerler daima değerinden geçer (kopyalanır). Bununla birlikte, bileşik değerleri referans ile iletilmektedir.

Peki bu alanı nasıl kopyalarız?

let arr = [1,2,3,4,5];

ES6'da Dizi Kopyalama

let arrCopy = [...arr]; 

ES5'te n Dizi kopyala

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

Neden 'arrCopy = arr` değeri tarafından geçmiyor?

Nesne / Dizi gibi Bileşik değerlerde bir değişkenin diğerine geçirilmesi farklı davranır. Copand değerlerinde asign işleci kullanarak bir nesneye başvuru iletiriz. Arr öğelerini kaldırırken / eklerken her iki dizinin de değerinin değişmesinin nedeni budur.

İstisnalar:

arrCopy[1] = 'adding new value this way will unreference';

Değişkene yeni bir değer atadığınızda, başvurunun kendisini değiştirirsiniz ve bu orijinal Nesne / Dizi'yi etkilemez.

daha fazla oku


6

Bildiğimiz gibi Javascript dizileri ve nesneleri referans içindir, ancak orijinal diziyi daha sonra değiştirmeden diziyi kopyalamanın ne yollarını yapabiliriz?

İşte bunu yapmanın birkaç yolu:

Kodunuzda bu dizinin bulunduğunu düşünün:

var arr = [1, 2, 3, 4, 5];

1) Bir işlevdeki dizi boyunca döngü yapmak ve şu şekilde yeni bir dizi döndürmek:

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }

2) Dilim yöntemini kullanarak, dilim dizinin bir bölümünü dilimlemek içindir, dizinin bir kısmını orijinaline dokunmadan dilimleyecek, dizinin başlangıcını ve bitişini belirtmezseniz, tüm dilimi dilimleyecektir dizi ve temel olarak dizinin tam bir kopyasını yapmak, böylece kolayca söyleyebiliriz:

var arr2 = arr.slice(); // make a copy of the original array

3) Ayrıca iletişim yöntemi, bu iki diziyi birleştirmek içindir, ancak dizilerden birini belirtebiliriz ve bu temel olarak yeni temas edilen dizideki değerlerin bir kopyasını oluşturur:

var arr2 = arr.concat();

4) Ayrıca dizgi ve ayrıştırma yöntemi, önerilmez, ancak Dizi ve Nesneleri kopyalamanın kolay bir yolu olabilir:

var arr2 = JSON.parse(JSON.stringify(arr));

5) Array.from yöntemi, bu yaygın olarak desteklenmez, kullanmadan önce farklı tarayıcılarda desteği kontrol edin:

const arr2 = Array.from(arr);

6) ECMA6 yolu, tam olarak desteklenmemektedir, ancak aktarmak istediğinizde babelJ'ler size yardımcı olabilir:

const arr2 = [...arr];

6
let a = [1,2,3];

Artık bir dizinin kopyasını oluşturmak için aşağıdakilerden herhangi birini yapabilirsiniz.

let b = Array.from(a); 

VEYA

let b = [...a];

VEYA

let b = new Array(...a); 

VEYA

let b = a.slice(); 

VEYA

let b = a.map(e => e);

Şimdi, eğer bir

a.push(5); 

Daha sonra a, [1,2,3,5] 'dir, ancak b farklı referansa sahip olduğu için hala [1,2,3]' tür.

Ama Array.from yukarıdaki tüm yöntemlerde daha iyi olduğunu ve esas olarak bir dizi kopyalamak için yapılmış düşünüyorum .


1
hangisi en hızlı?
Marc Frame


4

Benim durumumda bu benim için çalıştı böylece dizi sağlam kalmasını sağlamak gerekiyordu:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}

2
Aynı şeyi yapan, ancak daha az belirgin bir şekilde bir işlevi çağırarak kodunuza müstehcenlik eklemediğiniz için +1. dilim kaputun altında daha verimli olabilir, ancak kod üzerinde çalışan herkes için bu niyetinizi gösterir. artı (örneğin) kopyaladığınız şeyi filtrelemek istiyorsanız, daha sonra optimizasyonu kolaylaştırır. ancak bu derin kopyalamayı işlemez ve aynı dahili nesneler referans olarak yeni diziye aktarılır. Bu yapmak istediğiniz şey olabilir, olmayabilir.
senkronize olmayan

4

Çok boyutlu dizinin / nesnenin kopyasını oluşturun:

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

Bu işlev için James Padolsey'e teşekkürler.

Kaynak: İşte


3

Dan, süslü numaralar kullanmaya gerek yok. Tek yapmanız gereken bunu yaparak arr1'in kopyasını çıkarmak.

var arr2 = new Array(arr1);

Şimdi arr1ve arr2iki farklı dizi değişkeni ayrı yığınlarda saklanır. Bunu jsfiddle'da kontrol edin .


Bu diziyi kopyalamaz. Orijinaline (yani var arr2 = [arr1];) referans veren bir elemanlı bir dizi oluşturur .
Timothy003

3

Atama işlecini ( =) kullanarak bir diziyi kopyalamak istediğimizde bir kopya oluşturmaz, yalnızca işaretçiyi / başvuruyu diziye kopyalar. Örneğin:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

Genellikle verileri dönüştürdüğümüzde, ilk veri yapımızı (örneğin Dizi) sağlam tutmak istiyoruz. Bunu, dizimizin tam bir kopyasını oluşturarak yapıyoruz, böylece ilki bozulmadan dönüştürülebilir.

Diziyi kopyalama yolları:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
	const newArray = [];
    for(let i= 0; i < base.length; i++) {
	    newArray[i] = base[i];
	}
	return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

Diziler veya nesneler yuvalanırken dikkatli olun !:

Diziler yuvalandığında değerler referans olarak kopyalanır. Bunun sorunlara nasıl yol açabileceğine ilişkin bir örnek:

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

Bu nedenle, dizinizin içinde kopyalamak istediğiniz nesneler veya diziler olduğunda bu yöntemleri kullanmayın. yani bu yöntemleri sadece ilkel dizilerde kullanın.

Bir javascript dizisini deepclone JSON.parseile birlikte kullanmak istiyorsanız, şöyle JSON.stringify:

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

Kopyalama performansı:

Optimum performans için hangisini seçiyoruz. En ayrıntılı yöntem olan fordöngünün en yüksek performansa sahip olduğu ortaya çıktı . forGerçekten CPU yoğun kopyalama (büyük / çok diziler) için döngüyü kullanın .

Bundan sonra .slice()yöntem de iyi bir performansa sahiptir ve programcının uygulaması için daha az ayrıntılı ve daha kolaydır. .slice()Çok yoğun CPU içermeyen dizilerin günlük kopyalanması için kullanılmasını öneririm . Ayrıca, JSON.parse(JSON.stringify(arr))derin bir klon gerekli değilse ve performans sorunsa (çok fazla ek yük) kullanmaktan kaçının .

Kaynak performans testi


3

Diziniz, int, char veya dize vb. Gibi ilkel veri türünde öğeler içeriyorsa , orijinal dizinin .slice () veya .map () veya spread operatörü ( ES6 sayesinde).

new_array = old_array.slice()

veya

new_array = old_array.map((elem) => elem)

veya

const new_array = new Array(...old_array);

ANCAK diziniz nesneler (veya diziler) veya daha fazla iç içe nesne gibi karmaşık öğeler içeriyorsa , tüm öğelerin en üst düzeyden iç son referansa kadar bir kopyasını yaptığınızdan emin olmanız gerekir. nesneler kullanılır ve bu da new_array içindeki object_elements içindeki değerlerin değiştirilmesinin hala old_array öğesini etkileyeceği anlamına gelir. Bu seviyedeki kopyalama yöntemini , old_array öğesinin DEEP COPY ( KOPYALAMA KOPYALAMA ) olarak yapabilirsiniz.

Derin kopyalama için, veri türüne bağlı olarak her düzeyde ilkel veri türleri için yukarıda belirtilen yöntemleri kullanabilir veya fazla iş yapmadan derin bir kopya yapmak için bu pahalı yöntemi (aşağıda belirtilen) kullanabilirsiniz .

var new_array = JSON.parse(JSON.stringify(old_array));

Gereksinimlerinize bağlı olarak kullanabileceğiniz başka birçok yöntem var. Ben sadece bir dizi değeri diğerine kopyalamak çalıştığımızda ne olduğu hakkında genel bir fikir vermek için bunlardan sadece bahsetmiştim .


Çok teşekkürler, cevabınız benim senaryom için çalışan tek kişiydi,
albert sh

2

Bir nesnenin veya dizinin yeni bir kopyasını oluşturmak istiyorsanız, nesnenin özelliklerini veya dizinin öğelerini açıkça kopyalamanız gerekir, örneğin:

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

Değişmez ilkel değerler ve değişken nesne referansları hakkında Google hakkında daha fazla bilgi arayabilirsiniz.


1
Dizinin nesnelerinin özelliklerini açıkça kopyalamanız gerekmez. Chtiwi Malek'in cevabına bakın.
Magne

2

JQuery derin kopyasını kullanarak aşağıdaki gibi yapılabilir:

var arr2 = $.extend(true, [], arr1);

2

Array'ı kopyalamak için ES6 forma operatörünü de kullanabilirsiniz

var arr=[2,3,4,5];
var copyArr=[...arr];

2

Kopyalamanın birkaç yolu daha:

const array = [1,2,3,4];

const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );



2

Hızlı Örnekler:

  1. Dizideki öğeler ilkel türdeyse (dize, sayı vb.)

var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in 
// different places in the memory
var arr2 = arr1.slice(); 
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]

  1. Dizideki öğeler nesne değişmezindeyse, başka bir dizi ({}, [])

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice(); 
arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                 // deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                 // pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9;	 // not OK - same above reason

console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

  1. Çözüm 2 : Her öğe için Derin Kopyalama

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop();	  // OK - don't affect arr1
arr2[0].x = 'z';  // OK - don't affect arr1
arr2[1][0] = 9;	  // OK - don't affect arr1

console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]


1

İşte bir varyant:

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */

kötü bir fikir değil, ben daha iyi eval yerine JSON stringify / ayrıştırma kullanmak istiyorum, ve yine başka bir jsPerf karşılaştırmak kontrol etmek için iyi olurdu, ayrıca not toSourcestandart değildir ve örneğin Chrome'da çalışmaz.
dmi3y

1

Yeni tanıtılan var Array.from, ancak ne yazık ki, bu yazının yazıldığı tarih itibariyle sadece son Firefox sürümlerinde (32 ve üstü) destekleniyor. Basitçe aşağıdaki gibi kullanılabilir:

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

Referans: Burada

Veya Array.prototype.mapbir kimlik fonksiyonu ile kullanılabilir:

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

Referans: Burada


Bahsetmek için +1 Array.from, şimdi Internet Explorer ( kaynak ) hariç tüm büyük tarayıcılarda desteklenmektedir . .
mgthomas99

Unutmayın Array.fromderin kopyalama / derin klonlama sağlamaz, veri dönüşecek.
Sudhansu Choudhary

1

Nesneleri içeren ES6 dizisi için

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
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.