Nesne yayılımı ile Object.assign


396

Diyelim ki bir optionsdeğişkenim var ve bazı varsayılan değerler ayarlamak istiyorum.

Bu iki alternatifin yararı / dezavantajı nedir?

Nesne yayılmasını kullanma

options = {...optionsDefault, ...options};

Veya Object.assign kullanarak

options = Object.assign({}, optionsDefault, options);

Bu taahhüt beni meraklandırdı.


4
Birincisi, önerilen yeni bir sözdizimidir ve ES6'nın bir parçası değildir, bu nedenle hangi standarda bağlı kalmak istediğinize bağlıdır.
loganfsmyth

5
" En iyi " tanımlayın (dikkatle, fikir tabanlı bir soru ile bitmiyor :-)
Amit

1
Yerel desteği olmayan ortamlarda çalışıyorsa, nasıl desteklemek istediğinize de bağlı olabilir. Sözdizimi sadece derleyebilirsiniz. Çoklu doldurmanız gerekebilecek bir nesne veya yöntem.
JMM

6
Object.assign, uyumluluk sorunlarının dışında faydalı olan orijinal nesneyi değiştirebilir. yayılamaz.
pstanton

2
@ Pstanton'un comment - object.assign öğesini açıklığa kavuşturmak için mevcut bir hedef nesneyi değiştirebilir (diğer özellikleri olduğu gibi bırakırken özelliklerin üzerine yazabilirsiniz); kaynak nesneye dokunmaz . İlk olarak onun "orijinal nesnesini" "kaynak nesne" olarak okudum, bu yüzden bu notu benzer şekilde yanlış kullanan herkes için yazıyorum. :)
Araç

Yanıtlar:


328

Bu her zaman kapsamlı değildir.

Yaygın sözdizimi

options = {...optionsDefault, ...options};

Avantajları:

  • Yerel desteği olmayan ortamlarda yürütme için kod yazarsanız, bu sözdizimini derleyebilirsiniz (çok amaçlı dolgu kullanmak yerine). (Örneğin Babil ile.)

  • Daha az ayrıntılı.

Dezavantajları:

  • Bu cevap başlangıçta yazıldığında, bu standartlaştırılmamış bir teklifti . Teklifleri kullanırken, koduyla şimdi yazarsanız ve standardizasyona geçmezse veya standardizasyona geçtikçe değişiklik yaparsanız ne yapacağınızı düşünün. Bu, o zamandan beri ES2018'de standartlaştırılmıştır.

  • Değişmez, dinamik değil.


Object.assign()

options = Object.assign({}, optionsDefault, options);

Avantajları:

  • Standardize edilmiş.

  • Dinamik. Misal:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);

Dezavantajları:

  • Daha ayrıntılı.
  • Yerel desteği olmayan ortamlarda yürütme için kod yazarsanız, çoklu doldurmanız gerekir.

Bu beni şaşırtan şey.

Bu doğrudan sorduğunuz şeyle ilgili değil. Bu kod, aynı şeyi yapan Object.assign()kullanıcı kodunu ( object-assign) kullanıyordu . Bu kodu Babel ile derliyor gibi görünüyorlar (ve Webpack ile paketliyorlar), bahsettiğim şey bu: sadece derleyebileceğiniz sözdizimi. Görünüşe göre object-assign, yapılarına girecek bir bağımlılık olarak dahil edilmeyi tercih ettiler .


15
Nesne dinlenme yayılımının aşama 3'e taşındığını belirtmek gerekebilir, bu nedenle gelecekte gelecekte standartlaştırılacaktır twitter.com/sebmarkbage/status/781564713750573056
williaster

11
@JMM "Daha ayrıntılı" gördüğümü bilmiyorum. bir dezavantaj olarak.
DiverseAndRemote.com

6
@Omar iyi bir fikir olduğunu kanıtladınız :) Birisi bu avantajı tanımazsa o zaman evet, diğer her şey eşit olmak sadece kullanabilirsiniz Object.assign(). Ya da bir dizi nesne üzerinde manuel olarak yineleyebilir ve kendi sahne setlerini manuel olarak bir hedefe atayarak daha ayrıntılı hale getirebilirsiniz: P
JMM

7
@JMM'nin de belirttiği gibi, şimdi ES2018 spec node.green/#ES2018-features-object-rest-spread-properties
Sebastien H.

2
"Her bayt önemlidir" @yzorg için minimizers / uglify nedir
DiverseAndRemote.com

171

Referans nesne için dinlenme / yayılma ECMAScript 2018'de aşama 4 olarak sonlandırılır. Teklif burada bulunabilir .

Çoğunlukla nesne sıfırlama ve yayma çalışmaları aynı şekilde, temel fark, yayılmanın özellikleri tanımlaması ve Object.assign () tarafından ayarlanmasıdır . Bu, Object.assign () ayarlayıcılarını tetiklediği anlamına gelir.

Bunun dışında, nesne dinlenme / yayılma 1: 1 nesnesinin Object.assign () ile eşleştiğini ve dizi (yinelenebilir) yayılmaya farklı davrandığını hatırlamakta fayda var. Örneğin, bir diziyi yayarken null değerler yayılır. Ancak nesne yayılımı kullanıldığında null değerler sessizce hiçbir şeye yayılmaz.

Dizi (Yinelenebilir) Yayılma Örneği

const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;

console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError

Nesne Yayılım Örneği

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

Bu, Object.assign () işlevinin çalışma biçimiyle tutarlıdır; her ikisi de sessiz olarak null değeri hatasız olarak hariç tutar.

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}

10
Bu, seçilen cevap olmalıdır.
Evan Plaice

4
Cevap bu olmalı ... şimdi gelecek.
Zachary Abresch

1
Bu doğru cevap. Ana fark Object.assignayarlayıcıları kullanacak. Object.assign({set a(v){this.b=v}, b:2}, {a:4}); // {b: 4}vs.{...{set a(v){this.b=v}, b:2}, ...{a:4}}; // {a: 4, b: 2}
David Boho

1
"Gelecek şimdi!" - George Allen Yarın çok geç.
ruffin

1
Null'un farklı ele alındığının gösterilmesi "elma ve portakal" dır - anlamlı bir karşılaştırma değildir. Dizi durumunda null, dizinin bir öğesidir. Yayılma durumunda null nesnenin tamamıdır. X olması için doğru karşılaştırma olurdu boş özelliğini : const x = {c: null};. Bu durumda, AFAIK, aynı dizi gibi davranırdık //{a: 1, b: 2, c: null}.
ToolmakerSteve

39

Ben yayılma işleç arasındaki büyük bir fark Object.assignve geçerli yanıtlarda belirtilmemiş gibi görünüyor yayılma operatörünün kaynak nesnenin prototipini hedef nesneye kopyalamak olduğunu düşünüyorum. Bir nesneye özellikler eklemek istiyorsanız ve nesnenin hangi örneğini değiştirmek istemiyorsanız kullanmak zorundasınız Object.assign. Aşağıdaki örnek bunu göstermelidir:

const error = new Error();
error instanceof Error // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true


"prototipi olduğu gibi bırakmayacak" - kesinlikle hiçbir şeyi değiştirmediği için. Örneğin, errorExtendedUsingAssign === errorancak errorExtendedUsingSpreadyeni bir nesne (ve prototip kopyalanmadı).
maaartinus

2
@maaartinus Haklısın, muhtemelen bunu kötü ifade ettim. Prototipin kopyalanan nesnede olmadığı anlamına geliyordum. Bunu daha net olacak şekilde düzenleyebilirim.
Sean Dawson

Aşağıdakiler, sınıfı olan bir nesneyi "sığ klonlamak" için bir yol mudur? let target = Object.create(source); Object.assign(target, source);
ToolmakerSteve

@ToolmakerSteve Evet, nesnenin "kendi özelliklerini" etkili bir şekilde sığ bir klon olacak şekilde kopyalayacaktır. Bakınız: stackoverflow.com/questions/33692912/…
Sean Dawson

12

Diğerlerinin de belirttiği gibi, bu yazma anında, Object.assign()bir ...çoklu dolgu gerektirir ve nesne yayılması, çalışması için bir miktar nakil (ve belki de bir çoklu dolgu) gerektirir.

Bu kodu düşünün:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

Her ikisi de aynı çıktıyı üretir.

İşte Babil'den ES5'e çıktı:

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

Bu şimdiye kadarki anlayışım. Object.assign()aslında standartlaştırılmıştır, burada nesne dağılımı ...henüz değildir. Tek sorun, birincisi ve gelecekte, ikincisi için tarayıcı desteğidir.

Buradaki kodla oynayın

Bu yardımcı olur umarım.


1
Teşekkür ederim! Kod örneğiniz, bağlamım için kararı gerçekten kolaylaştırır. Transpiler (babel veya daktilo) satır içi koduna bir polly dolgu ekleyerek yayma operatörünü tarayıcılarla daha uyumlu hale getirir. Sadece ilgi için TypeScript aktarılan sürüm neredeyse Babel ile aynıdır: typescriptlang.org/play/…
Mark Whitfeld

2
hmm ... iki vakanız aynı sonuçları vermeyecek mi? ilk durumda özellikleri bir nesneden diğerine kopyalarsınız ve diğerinde yeni bir nesne yaratırsınız. Object.assign hedefi döndürür, bu nedenle ilk durumda objAss ve newObjAss aynıdır.
Kevin B

Yeni bir ilk parametresi eklemek {}tutarsızlığı düzeltmelidir.
Kevin B

11

Nesne yayma işleci (...) tarayıcılarda çalışmaz, çünkü henüz herhangi bir ES spesifikasyonunun parçası değil, sadece bir tekliftir. Tek seçenek Babil (veya benzer bir şey) ile derlemektir.

Gördüğünüz gibi, Object.assign ({}) üzerindeki sözdizimsel şeker.

Görebildiğim kadarıyla bunlar önemli farklar.

  • Object.assign çoğu tarayıcıda çalışır (derlemeden)
  • ... nesneler için standart değil
  • ... sizi nesneyi yanlışlıkla değiştirmeye karşı korur
  • ... Object.assign tarayıcıları onsuz çoklu dolduracak
  • ... aynı fikri ifade etmek için daha az koda ihtiyaç var

34
Bu sözdizimsel şeker değildirObject.assign , çünkü yayma operatörü her zaman yeni bir nesne verecektir.
MaxArt

4
Aslında, diğer insanların değişebilirlik farkını daha fazla vurgulamadığına şaşırdım. Tüm geliştirici saatlerini
Object.assign

Bu, çoğu modern tarayıcıda (diğer ES6'da olduğu gibi) desteklenmektedir: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
akmalhakimi1991

11

Tarayıcılarda ve ekosistemdeki "yayılma nesnesi birleştirme" ES özelliğinin durumunu araçlar aracılığıyla özetlemek istiyorum.

spec

Tarayıcılar: Chrome'da, yakında SF'de, Firefox'ta (ver 60, IIUC)

  • Bu senaryo dahil , Chrome 60'ta gönderilen "yayılma özellikleri" için tarayıcı desteği .
  • Bu senaryo için destek geçerli Firefox'ta (59) çalışmaz, ancak Firefox Developer Edition'ımda çalışır. Bu yüzden Firefox 60'da gönderileceğine inanıyorum.
  • Safari: test edilmedi, ancak Kangax Masaüstü Safari 11.1'de çalıştığını ancak SF 11'de çalışmadığını söylüyor
  • iOS Safari: kesin değil, ancak Kangax iOS 11.3'te çalıştığını ancak iOS 11'de çalışmadığını söylüyor
  • henüz Edge'de değil

Araçlar: Düğüm 8.7, TS 2.1

Bağlantılar

Kod Örneği (uyumluluk testi olarak iki katına çıkar)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

Tekrar: Bu örneği yazarken Chrome (60+), Firefox Developer Edition (Firefox 60 önizlemesi) ve Düğümde (8.7+) transpilasyon olmadan çalışır.

Neden Cevap Verilmeli?

Bunu orijinal sorudan 2,5 yıl sonra yazıyorum . Ama ben de aynı soruyu sordum ve Google bu noktada beni gönderdi. Ben SO'nun uzun kuyruğunu geliştirme görevinin bir kölesiyim.

Bu "dizi yayılımı" sözdiziminin bir genişleme olduğundan, google çok zor ve uyumluluk tablolarında bulmak zor buldum. Ben bulabildiğim en yakın Kangax "özellik yayılması" olduğunu , ancak bu test aynı ifadede (bir birleştirme değil) iki yayılması yoktur. Ayrıca, teklifler / taslaklar / tarayıcı durum sayfalarındaki adın tümü "mülk yayılımı" kullanır, ancak bana "nesne birleştirme" için yayılım sözdizimi kullanma önerilerinin ardından topluluğun ulaştığı bir "ilk müdür" gibi görünüyor. (Bu da google'ın bu kadar zor olmasının nedenini açıklayabilir.) Bu yüzden başkalarının bu özellikle ilgili bağlantıları görüntüleyebilmesi, güncelleyebilmesi ve derleyebilmesi için bulgumu burada belgeliyorum. Umarım yakalar. Lütfen spesifikasyonlara ve tarayıcılara iniş haberi yayılmasına yardımcı olun.

Son olarak, bu bilgiyi yorum olarak eklerdim, ancak yazarların orijinal amacını bozmadan bunları düzenleyemezdim. Özellikle, @ ChillyPenguin'in yorumunu @RichardSchulte'yi düzeltme niyetini kaybetmeden düzenleyemiyorum. Ama yıllar sonra Richard haklı çıktı (bence). Bu yüzden bunun yerine bu cevabı yazıyorum, sonunda eski cevaplar üzerinde çekiş kazanacağını umuyorum (yıllar alabilir, ancak sonuçta uzun kuyruk etkisinin nedeni budur ).


3
"neden cevap" bölümünüz muhtemelen gerekli değildir
LocustHorde

@LocustHorde Belki 2. paragrafı (bu konunun google için bu kadar zor olmasının nedeni) kendi bölümüne taşıyabilirim. Sonra geri kalanlar bir yoruma sığabilir.
yzorg

8

Not: forma sadece Object.assign etrafında sözdizimsel şeker değildir. Sahne arkasında çok farklı çalışırlar.

Object.assign yeni bir nesneye ayarlayıcılar uygular, Spread uygulanmaz. Ayrıca, nesne yinelenebilir olmalıdır.

Kopyala Nesnenin değerine şu anda olduğu gibi ihtiyacınız varsa ve bu değerin nesnenin diğer sahipleri tarafından yapılan değişiklikleri yansıtmasını istemiyorsanız bunu kullanın.

Her zaman kopyalanacak değişmez özellikleri ayarlamak için iyi uygulama nesnesinin sığ bir kopyasını oluşturmak için kullanın.

Ata Ata, kopyalamanın tam tersidir. Ata, değeri kopyalamak veya tutmak yerine, örnek değişkenine doğrudan atayan bir ayarlayıcı oluşturur. Assign özelliğinin alıcısını çağırırken, gerçek verilere bir başvuru döndürür.


3
Neden "Kopyala" diyor? Bu cesur başlıklar nelerdir. Bunu okurken bir bağlamı kaçırmışım gibi hissediyorum ...
ADJenks

2

Diğer cevaplar eski, iyi bir cevap alınamadı.
Aşağıdaki örnek nesne değişmez değerleri içindir, her ikisinin birbirini nasıl tamamlayabildiğine ve birbirini nasıl tamamlayamadığına yardımcı olur (bu nedenle fark):

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

Dizi ve nesne için de birkaç örnek daha:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax


1

Bu artık ES6'nın bir parçasıdır, bu nedenle standartlaştırılmıştır ve MDN'de de belgelenmiştir: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

Kullanımı çok uygundur ve nesne yıkımının yanında çok mantıklıdır.

Yukarıda listelenen avantajlardan biri Object.assign () öğesinin dinamik özellikleridir, ancak bu, diziyi değişmez bir nesnenin içine yaymak kadar kolaydır. Derlenmiş babel çıktısında Object.assign () ile gösterilenleri kullanır

Bu nedenle doğru cevap, artık standartlaştırıldığı, yaygın olarak kullanıldığı (bkz. Tepki, redux, vb.), Kullanımı kolay ve Object.assign () öğesinin tüm özelliklerine sahip olduğundan nesne yayılmasını kullanmak olacaktır.


2
Hayır, ES6'nın bir parçası değil. Sağladığınız bağlantı, forma operatörünün yalnızca dizilerdeki kullanımını ifade eder . Forma operatörünün nesnelerde kullanımı şu anda JMM'nin cevabında açıklandığı gibi bir Aşama 2 (Taslak) önerisidir.
ChillyPenguin

1
Tamamen yanlış bilgilere dayanan bir cevap gördüğümü sanmıyorum. Bir yıl sonra bile, ES spesifikasyonunun bir parçası değildir ve çoğu ortamda desteklenmez.
3ocene

Chilly ve 3o'nun yanlış olduğu ortaya çıktı, Richard doğru. Tarayıcı desteği ve araç desteği hepsi iniş halindedir, ancak Richard'ın cevabından 1,5 yıl sonra gerçekleşmiştir. Mart 2018 itibariyle destek özeti için yeni cevabımı görün.
yzorg

0

Object.assign kullanmanız gerektiğinde bu basit örneği eklemek istiyorum.

class SomeClass {
  constructor() {
    this.someValue = 'some value';
  }

  someMethod() {
    console.log('some action');
  }
}


const objectAssign = Object.assign(new SomeClass(), {});
objectAssign.someValue; // ok
objectAssign.someMethod(); // ok

const spread = {...new SomeClass()};
spread.someValue; // ok
spread.someMethod(); // there is no methods of SomeClass!

JavaScript kullandığınızda net olmayabilir. Ancak TypeScript ile, bir sınıfın örneğini oluşturmak istiyorsanız daha kolaydır

const spread: SomeClass = {...new SomeClass()} // Error
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.