JavaScript'te Harita ve Nesne Karşılaştırması


290

Sadece chromestatus.com'u keşfettim ve günün birkaç saatini kaybettikten sonra bu özellik girişini buldum :

Harita: Harita nesneleri basit anahtar / değer haritalarıdır.

Bu beni şaşırttı. Normal JavaScript nesneleri sözlüktür, bu yüzden sözlükten Mapfarkı nedir? Kavramsal olarak, özdeştirler ( Bir Harita ile Sözlük arasındaki fark nedir? )

Chrometatus dokümantasyon referansları şunlara da yardımcı olmaz:

Eşleme nesneleri, hem anahtarların hem de değerlerin keyfi ECMAScript dil değerleri olabileceği anahtar / değer çiftlerinin koleksiyonlarıdır. Farklı bir anahtar değeri, Harita koleksiyonundaki yalnızca bir anahtar / değer çiftinde ortaya çıkabilir. Harita oluşturulduğunda seçilen bir karşılaştırma algoritması kullanılarak farklı anahtar değerleri ayırt edildi.

Bir Map nesnesi, öğelerini ekleme sırasında yineleyebilir. Eşleme nesnesi, karma tablolar veya koleksiyondaki öğelerin sayısına alt doğrusal olan erişim süreleri sağlayan diğer mekanizmalar kullanılarak uygulanmalıdır. Bu Harita nesneleri spesifikasyonunda kullanılan veri yapılarının yalnızca Harita nesnelerinin gerekli gözlemlenebilir anlamlarını tanımlaması amaçlanmıştır. Geçerli bir uygulama modeli olması amaçlanmamıştır.

… Hala bana bir nesne gibi geliyor, bu yüzden açıkça bir şey kaçırdım.

JavaScript neden (iyi desteklenen) bir Mapnesne kazanıyor ? Bu ne işe yarıyor?


Yanıtlar:


286

Mozilla'ya göre:

Bir Map nesnesi, öğelerini ekleme sırasında yineleyebilir - for..of döngüsü her yineleme için bir [anahtar, değer] dizisi döndürür.

ve

Nesneler, anahtarları değerlere ayarlamanıza, bu değerleri almanıza, anahtarları silmenize ve bir anahtarda bir şeyin depolanıp depolanmadığını belirlemenize olanak tanıdığı için Haritalar'a benzer. Bu nedenle, nesneler tarihsel olarak Haritalar olarak kullanılmıştır; ancak, bir Harita kullanımını daha iyi hale getiren Nesneler ve Haritalar arasında önemli farklılıklar vardır.

Bir Nesnenin bir prototipi vardır, bu nedenle haritada varsayılan anahtarlar vardır. Ancak, bu map = Object.create (null) kullanılarak atlanabilir. Bir Nesnenin anahtarları, bir Harita için herhangi bir değer olabileceği Dizelerdir. Bir Nesnenin boyutunu manuel olarak izlemek zorunda kalırken bir Harita boyutunu kolayca elde edebilirsiniz.

Anahtarlar çalışma süresine kadar bilinmiyorsa ve tüm anahtarlar aynı türdeyse ve tüm değerler aynı türdeyse, nesneler üzerinde eşlemeler kullanın.

Tek tek öğeler üzerinde çalışan bir mantık olduğunda nesneleri kullanın.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

Sırayla yinelenebilirlik, kısmen tüm tarayıcılarda aynı performansı sağladığından, geliştiriciler tarafından uzun zamandır istenen bir özelliktir. Bana göre bu büyük bir şey.

myMap.has(key)Yöntem özellikle kullanışlı ve aynı zamanda olacaktır myMap.sizeözelliği.


13
Muhtemelen bir dezavantaj, bir Harita'nın ekleme sırasını korumak için daha fazla bellek (ancak aynı büyüklük sırasına göre) gerektirmesidir.
John Kurlak

4
Haritalar, burada belirtilen sıralamanın yanı sıra (anahtar olarak herhangi bir nesneyi kullanma, anahtarların ve desteklerin ayrılması, vb.) Başka özelliklere sahiptir, ancak bazı durumlarda FWIW düz nesne özelliklerinin yineleme sırası ES2015 tarafından tanımlanır. Bkz. Stackoverflow.com/a/32149345 .
JMM

2
Bir nesnenin bir prototipimap = Object.create(null) olduğunu söylediğinizde, anlamını anlayamadım , bu yüzden haritada varsayılan anahtarlar var. Ancak, bu kullanılarak atlanabilir . Varsayılan anahtarlar nedir? Anahtarlar nasıl ilişkilidir Object.prototype?
Aralık'ta aşırı değişim

4
Chrome'daki testlerim, haritaların düzeni sağlamak için önemli miktarda daha fazla bellek kullanmamalarını gösterdi. Bir milyon anahtar için 0.1 KB daha fazla bir şey vardı ve bunun düzeni korumak için olduğunu düşünmüyorum. Ancak, ~ 0.1KB sabit bir ek yük gibi görünüyor. Bunun yerine bir anahtarla bir milyon harita oluşturursanız ve bunu nesneden çok daha büyük olarak karşılaştırırsanız.
jgmjgm

2
@luxon orada bir nesne oluşturuyorsun. ES6 spec, bir harita nesnesi oluşturmak için newoperatörün Mapsembolü ile kullanılmasını gerektirir new Map. var a = {}(kısaca eşdeğer) için anlamvar a = Object.create(Object.prototype)
dudewad

104

Temel fark, Nesnelerin yalnızca Haritalar'ın herhangi bir anahtar türünü az ya da çok desteklediği dize anahtarlarını desteklemesidir.

Ben yaparsanız obj[123] = trueo zaman ve Object.keys(obj)o zaman alacak ["123"]yerine [123]. Bir Harita, anahtarın türünü ve [123]büyük olan dönüşü koruyacaktır . Haritalar ayrıca Nesneleri anahtar olarak kullanmanızı sağlar. Geleneksel olarak bunu yapmak için nesnelere onları hash etmek için benzersiz bir tanımlayıcı vermelisiniz ( getObjectIdstandardın bir parçası olarak JS'de hiç bir şey görmedim sanmıyorum ). Haritalar ayrıca siparişin korunmasını garanti eder, bu nedenle koruma için her şey daha iyi olur ve bazen birkaç çeşit yapmanız gerektiğini kaydedebilirsiniz.

Pratikte haritalar ve nesneler arasında çeşitli artılar ve eksiler vardır. Nesneler, JavaScript'in çekirdeğine çok sıkı bir şekilde entegre olmanın hem avantajlarını hem de dezavantajlarını kazanır ve bu da onları önemli destek farkının ötesinde önemli ölçüde Haritadan ayırır.

Acil bir avantaj, nesneler için sözdizimsel desteğe sahip olmanızdır. JSON ile de doğrudan destek alabilirsiniz. Karma olarak kullanıldığında, herhangi bir özelliği olmayan bir nesneyi elde etmek can sıkıcıdır. Varsayılan olarak Nesneleri bir karma tablosu olarak kullanmak istiyorsanız, bunlar kirlenir ve hasOwnPropertyözelliklere erişirken genellikle bunları çağırmanız gerekir. Burada varsayılan olarak Nesnelerin nasıl kirlendiğini ve karma olarak kullanılmak üzere umarım kirlenmemiş nesnelerin nasıl oluşturulacağını görebilirsiniz:

({}).toString
    toString() { [native code] }
JSON.parse('{}').toString
    toString() { [native code] }
(Object.create(null)).toString
    undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
    undefined

Nesneler üzerindeki kirlilik, yalnızca kodu daha can sıkıcı, daha yavaş, vb hale getiren bir şey değil, aynı zamanda güvenlik için potansiyel sonuçlara yol açabilir.

Nesneler saf karma tablolar değildir ancak daha fazlasını yapmaya çalışırlar. hasOwnPropertyUzunluğunu kolayca elde edememek gibi baş ağrılarınız var ( Object.keys(obj).length) vb. Nesnelerin yalnızca karma haritalar olarak değil, dinamik genişletilebilir Nesneler olarak kullanılması amaçlanmıştır ve bu yüzden onları saf karma tablolar olarak kullandığınızda sorunlar ortaya çıkar.

Karşılaştırma / Çeşitli genel işlemlerin listesi:

    Object:
       var o = {};
       var o = Object.create(null);
       o.key = 1;
       o.key += 10;
       for(let k in o) o[k]++;
       var sum = 0;
       for(let v of Object.values(m)) sum += v;
       if('key' in o);
       if(o.hasOwnProperty('key'));
       delete(o.key);
       Object.keys(o).length
    Map:
       var m = new Map();
       m.set('key', 1);
       m.set('key', m.get('key') + 10);
       m.foreach((k, v) => m.set(k, m.get(k) + 1));
       for(let k of m.keys()) m.set(k, m.get(k) + 1);
       var sum = 0;
       for(let v of m.values()) sum += v;
       if(m.has('key'));
       m.delete('key');
       m.size();

Farklı inişler ve çıkışlar (performans, kısa, taşınabilir, uzatılabilir, vb.) İle birkaç seçenek, yaklaşım, yöntem, vb. Vardır. Nesneler, dilin çekirdeğini oluşturmak için biraz gariptir, bu nedenle onlarla çalışmak için birçok statik yönteminiz vardır.

Ayrıca, anahtar türlerini koruyan Haritalar'ın yanı sıra nesneler gibi şeyleri, nesnelerin sahip olduğu yan etkilerden izole edilmiş anahtarlar olarak destekleyebilmesinin yanı sıra. Bir Harita saf bir karmadır, aynı zamanda bir nesne olmaya çalışmak konusunda karışıklık yoktur. Haritalar, proxy işlevleriyle de kolayca genişletilebilir. Nesne şu anda bir Proxy sınıfına sahiptir, ancak performans ve bellek kullanımı çok zordur, aslında Nesneler için Harita'ya şu anda Proxy'den daha iyi performans gösteren kendi proxy'nizi oluşturmak.

Haritalar için önemli bir dezavantaj, bunların doğrudan JSON ile desteklenmemesidir. Ayrıştırma mümkündür, ancak birkaç videoyla sohbete sahiptir:

JSON.parse(str, (k,v) => {
    if(typeof v !== 'object') return v;
    let m = new Map();
    for(k in v) m.set(k, v[k]);
    return m;
});

Yukarıdakiler ciddi bir performans isabeti sunacak ve herhangi bir dize anahtarını desteklemeyecektir. JSON kodlaması daha da zor ve problemlidir (bu birçok yaklaşımdan biridir):

// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
    return JSON.stringify({
        keys: Array.from(this.keys()),
        values: Array.from(this.values())
    });
};

Bu yalnızca Haritalar kullanıyorsanız o kadar da kötü değildir, ancak türleri karıştırırken veya skaler olmayan değerleri anahtar olarak kullanırken sorun yaşayacaktır (JSON, IE dairesel nesne başvurusu gibi bu tür bir sorunla mükemmel değildir). Ben test etmedim ama şansı stringize göre performansı ciddi zarar vardır.

Diğer komut dosyası dillerinde genellikle Harita, Nesne ve Dizi için açık skaler olmayan türler olduğu için sorun yoktur. Web geliştirme genellikle PHP için Array / Map nesnelerini özellikleri için A / M kullanarak ve JS Map / Object'i Array ile M / O genişleterek birleştirir. Karmaşık türleri birleştirmek, şeytanın yüksek seviyeli kodlama dillerinden oluşan çetesidir.

Şimdiye kadar bunlar büyük ölçüde uygulama ile ilgili konulardır, ancak temel operasyonlar için performans da önemlidir. Performans da karmaşıktır çünkü motora ve kullanıma bağlıdır. Herhangi bir hatayı ekleyemediğim için testlerimi bir tuz tanesi ile yap (bunu acele etmeliyim). Ayrıca benimkini sadece kaba bir endikasyon vermek için sadece çok spesifik basit senaryoları incelediği için onaylamanız gerekir. Chrome'da çok büyük nesneler / haritalar için yapılan testlere göre, silme nedeniyle nesnelerin performansı daha kötüdür; görünüşe göre O (1) yerine anahtar sayısıyla bir şekilde orantılıdır:

Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2

Chrome'un alma ve güncelleme konusunda açıkça güçlü bir avantajı var, ancak silme performansı korkunç. Haritalar bu durumda az miktarda daha fazla bellek kullanır (havai), ancak yalnızca bir Nesne / Harita milyonlarca anahtarla test edildiğinde, haritalar için ek yükün etkisi iyi ifade edilmez. Bellek yönetimi ile nesneleri doğru bir şekilde okuyorsam, nesnelerin lehine bir fayda olabilir daha önce ücretsiz gibi görünüyor.

Bu belirli kıyaslama için Firefox'ta farklı bir hikaye:

Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1

Hemen bu özel ölçütte Firefox'taki nesnelerden silinmenin herhangi bir soruna neden olmadığını, ancak diğer ölçütlerde özellikle Chrome'da olduğu gibi birçok anahtar olduğunda sorunlara neden olduğunu belirtmeliyim. Haritalar, büyük koleksiyonlar için Firefox'ta açıkça üstündür.

Ancak bu hikayenin sonu değil, pek çok küçük nesne veya harita ne olacak? Bu hızlı bir kıyaslama yaptım ama kapsamlı bir (ayar / alma) değil yukarıdaki işlemleri az sayıda tuşları ile en iyi performans. Bu test daha çok bellek ve başlatma ile ilgilidir.

Map Create: 69    // new Map
Object Create: 34 // {}

Yine bu rakamlar farklılık gösterir, ancak temelde Object iyi bir ipucuna sahiptir. Bazı durumlarda, nesnelerin harita üzerindeki önceliği aşırıdır (~ 10 kat daha iyi), ancak ortalama 2-3 kat daha iyiydi. Aşırı performans artışları her iki yönde de işe yarayabilir. Bunu yalnızca Chrome'da ve profil kullanımı ve ek yükü oluşturmak için oluşturmada test ettim. Chrome'da, bir tuşa sahip Haritalar'ın bir tuşa sahip Nesneler'den yaklaşık 30 kat daha fazla bellek kullandığını görünce oldukça şaşırdım.

Yukarıdaki işlemlerin birçoğunu test etmek için (4 tuş):

Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139

Bellek tahsisi açısından, bunlar boşaltma / GC açısından aynı şekilde davrandılar, ancak Harita 5 kat daha fazla bellek kullandı. Bu testte 4 anahtar kullanıldı, burada son testte olduğu gibi sadece bir anahtar ayarladım, böylece bu bellek yükündeki azalmayı açıklayacaktı. Bu testi birkaç kez çalıştırdım ve Harita / Nesne, genel hız açısından Chrome için genel olarak az ya da çok boyun ve boyun. Küçük Nesneler için Firefox'ta, genel olarak haritalara göre kesin bir performans avantajı vardır.

Bu elbette çılgınca değişebilecek bireysel seçenekleri içermez. Bu rakamlarla mikro optimizasyon önermem. Bundan kurtulabileceğiniz şey, genel bir kural olarak, çok büyük anahtar / değer depoları için Haritalar'ı ve küçük anahtar / değer depoları için nesneleri daha güçlü bir şekilde ele almanızdır.

Bunun ötesinde bu iki ile en iyi strateji onu uygulamak ve sadece ilk işe yapmak. Profil oluştururken, nesne anahtar silme durumunda görülen motor tuhaflıkları nedeniyle onlara bakarken bazen yavaş olacağını düşünmediğiniz şeylerin inanılmaz derecede yavaş olabileceğini akılda tutmak önemlidir.


Serileştirilebilirlik eksikliği birçok geliştirici için gerçek bir acı olmuştur. Ait upvote bak ben (başka bir yerde veya) localStorage bir ES6 Harita devam nasıl? ve nasıl bir ES6 Haritası JSON.stringify? .
Franklin Yu

Sayı milisaniye, bayt veya toplam nesne olarak mı?
StefansArya

Ms aldı (bir şey kullanılan bir şey söylemek için kısadır, bu nedenle bu durumda zaman kullanır). Bu eski bir test olmasına rağmen ve artık karşılaştırma kodu yok. Muhtemelen şimdi çok farklı. Mesela silme sorunu giderildi.
jgmjgm

27

Şimdiye kadar cevaplarda şu noktalardan bahsedildiğini sanmıyorum ve bahsetmeye değer olduklarını düşündüm.


Haritalar daha büyük olabilir

Krom ben alabilirim 16.7 ile milyon anahtar / değer çiftleri Mapvs. 11.1 düzenli cisimle milyon. Neredeyse% 50 daha fazla çift ile Map. Her ikisi de çökmeden önce yaklaşık 2GB bellek kaplıyorlar ve bu yüzden kromun bellek sınırlamasıyla ilgili olabileceğini düşünüyorum ( Düzenle : Evet, 2'yi doldurmayı deneyin ve çökmeden Mapsönce her biri 8.3 milyon çift olsun). Bunu bu kodla kendiniz test edebilirsiniz (bunları aynı anda değil ayrı olarak çalıştırın):

var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
// versus:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}

Nesnelerin bazı özellikleri / anahtarları zaten var

Bu beni daha önce harekete geçirdi. Düzenli nesneler var toString, constructor, valueOf, hasOwnProperty, isPrototypeOfve diğer önceden varolan özelliklerin bir demet. Bu, çoğu kullanım durumunda büyük bir sorun olmayabilir, ancak daha önce benim için sorunlara neden oldu.

Haritalar daha yavaş olabilir:

Nedeniyle .getişlev çağrısı yükü ve iç optimizasyon eksikliği, Harita ölçüde daha yavaş olabilir bazı görevler için bir düz eski JavaScript nesnesi daha.


1
Sizce anlambilim buradaki performanstan daha ağır mı? Bir sözlüğe ihtiyacınız varsa haritalar mükemmel görünür, ancak daha yavaş bir aramayı kabul etmek zordur. Sözlüklerin tamamı hızlı bir arama değil midir?
user2954463

3
Eğer 11 milyon anahtar / değer çiftleri birliktesin cezası ve benzeri önceden varolan anahtarları umurumda değil eğer ben kesinlikle düz eski nesnelerle gider toString, constructorvb (yani sizin tuşları onlarla çarpmasıyla, son derece olası değildir). Örneğin artıştır - Onlar ile çalışmak kolay olacak obj[i] = (obj[i] || 0) + 1, oysa Mapo en map.set(i, (map.get(i) || 0) + 1)hâlâ çok kötü olmadığı, ama sadece gösterileri şeyler gereksiz dağınık becerebiliyorolman. Haritalar kesinlikle kullanım durumlarına sahiptir, ancak genellikle düz bir nesne yapar.

1
Varsayılan kurtulmak anlamına Not toString, constructoryazarak, (vs.) nesne özellikleri obj = Object.create(null)yerine obj = {}.

18

Javascript dinamik olarak yazıldığından nesneler sözlük gibi davranabilir, böylece istediğiniz zaman bir nesneye özellik ekleyebilir veya kaldırabilirsiniz.

Ancak yeni Map()işlevsellik çok daha iyi çünkü:

  • Sağladığı get, set, has, ve deleteyöntemleri.
  • Yalnızca dizeler yerine anahtarlar için herhangi bir türü kabul eder.
  • Kolay for-ofkullanım için bir yineleyici sağlar ve sonuçların sırasını korur.
  • Yineleme veya kopyalama sırasında gösterilen prototiplerin ve diğer özelliklerin bulunduğu uç durumlar yoktur.
  • Milyonlarca öğeyi destekler.
  • Javascript motorları iyileştikçe çok hızlı ve daha hızlı olmaya devam ediyor.

Zamanın% 99'u sadece a Map(). Ancak, yalnızca dizeye dayalı anahtarlar kullanıyorsanız ve maksimum okuma performansına ihtiyacınız varsa, nesneler daha iyi bir seçim olabilir.

Detay (hemen hemen tüm) javascript motorları arka planda C ++ sınıfları aşağı nesneleri derlemek olduğunu. Bu türler "anahatları" tarafından önbelleğe alınır ve yeniden kullanılır, bu nedenle aynı özelliklere sahip yeni bir nesne oluşturduğunuzda motor mevcut bir arka plan sınıfını yeniden kullanır. Bu sınıflardaki özellikler için erişim yolu çok optimize edilmiş ve a Map().

Bir özellik eklemek veya kaldırmak, önbelleğe alınmış yedekleme sınıfının yeniden derlenmesine neden olur, bu nedenle bir nesneyi çok sayıda anahtar ekleme ve silme ile sözlük olarak kullanmak çok yavaştır, ancak nesneyi değiştirmeden mevcut anahtarların okunması ve atanması çok hızlıdır.

Dize anahtarları ile bir kez yazılır bir okuma ağır iş yükünüz varsa, objectözel bir yüksek performanslı sözlük olarak kullanın, ancak diğer her şey için a kullanın Map().


Nesne get set has deletevb işlevsellik de sağlar, o kadar zarif değil (ama kötü de değil). MapYineleme için hangi yolla kullanmak daha kolaydır? Kabul edebileceğimden emin değilim.
Andrew

@Andrew yöntemleri alıyorum ve ne kullandığınıza ve sonuca bağlı olarak işlevsellik de farklı. Yineleme daha kolaydır çünkü prototip ve yerel özellikler döngüde görünmez ve aynı sırayı koruyan normal bir JS yineleyicisi kullanır.
Mani Gandham

11

Diğer yanıtlara ek olarak, Haritalar'ın nesnelerle çalışmaktan daha hantal ve ayrıntılı olduğunu gördüm.

obj[key] += x
// vs.
map.set(map.get(key) + x)

Bu önemlidir, çünkü daha kısa kod daha hızlı okunur, daha doğrudan ifade edilir ve programcının kafasında daha iyi tutulur .

Başka bir yönü: set (), değeri değil haritayı döndürdüğü için atamaları zincirlemek imkansızdır.

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

Haritalarda hata ayıklama da daha acı vericidir. Aşağıda, haritada hangi tuşların olduğunu gerçekten göremezsiniz. Bunu yapmak için kod yazmanız gerekir.

Harita Yineleyicisini değerlendirmede iyi şanslar

Nesneler herhangi bir IDE tarafından değerlendirilebilir:

Bir nesneyi değerlendiren WebStorm


4
Tüm bunlar göz önüne alındığında, harita erken bir optimizasyon gibi görünüyor.
PRMan

10

Özet:

  • Object: Verilerin anahtar / değer çiftleri olarak depolandığı bir veri yapısı. Bir nesnede anahtarın bir sayı, dize veya simge olması gerekir. Değeri bu yüzden de şey diğer amaçları olabilir, fonksiyonlar vs. amacı, bir sigara sipariş hatırlanmıyorsa veri yapısı, temel değer çiftleri sokulması, yani sekans
  • ES6 Map: Verilerin anahtar / değer çiftleri olarak depolandığı bir veri yapısı. Hangi benzersiz bir anahtar bir değere eşler . Hem anahtar hem de değer herhangi bir veri türünde olabilir . Harita yinelenebilir bir veri yapısıdır, yani ekleme dizisinin hatırlandığı ve örneğin bir for..ofdöngüdeki öğelere erişebileceğimiz anlamına gelir

Temel farklılıklar:

  • A Mapsıralı ve yinelenebilir, oysa bir nesne sipariş edilmez ve yinelenemez

  • Herhangi bir veri türünü Mapanahtar olarak koyabiliriz , oysa nesnelerin anahtar olarak yalnızca bir sayı, dize veya sembolü olabilir.

  • Bir Mapmiras Map.prototype. Bu, Mapnesnelerle çalışmayı çok daha kolay hale getiren her türlü yardımcı işlev ve özellik sunar .

Misal:

nesne:

let obj = {};

// adding properties to a object
obj.prop1 = 1;
obj[2]    =  2;

// getting nr of properties of the object
console.log(Object.keys(obj).length)

// deleting a property
delete obj[2]

console.log(obj)

Harita:

const myMap = new Map();

const keyString = 'a string',
    keyObj = {},
    keyFunc = function() {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

console.log(myMap.size); // 3

// getting the values
console.log(myMap.get(keyString));    // "value associated with 'a string'"
console.log(myMap.get(keyObj));       // "value associated with keyObj"
console.log(myMap.get(keyFunc));      // "value associated with keyFunc"

console.log(myMap.get('a string'));   // "value associated with 'a string'"
                         // because keyString === 'a string'
console.log(myMap.get({}));           // undefined, because keyObj !== {}
console.log(myMap.get(function() {})) // undefined, because keyFunc !== function () {}

Kaynak: MDN


4

İyi tanımlanmış bir düzende yinelenebilir olmanın yanı sıra (hariç -0) anahtar olarak keyfi değerleri kullanabilme yeteneğine ek olarak , haritalar aşağıdaki nedenlerden dolayı yararlı olabilir:

  • Teknik özellik, harita işlemlerinin ortalama olarak alt doğrusal olmasını zorunlu kılar.

    Nesnenin aptal olmayan bir uygulaması bir karma tablo veya benzeri kullanacaktır, bu nedenle özellik aramaları muhtemelen ortalama olarak sabit olacaktır. Böylece nesneler haritalardan bile daha hızlı olabilir. Ancak bu şartname gerektirmez.

  • Nesneler, beklenmedik davranışlara sahip olabilir.

    Örneğin, fooyeni oluşturulan bir nesneye herhangi bir özellik ayarlamadığınızı varsayalım obj, obj.footanımsız olarak dönmeyi beklersiniz . Ama fooyerleşik miras inşa edilebilir Object.prototype. Veya obj.foobir ödev kullanarak oluşturmaya çalışıyorsunuz , ancakObject.prototype çalışıyorsunuz değerlerinizi depolamak yerine çalışıyor.

    Haritalar bu tür şeyleri engeller. Bazı senaryolarla uğraşmadıkça Map.prototype. Ve Object.create(null)de işe yarayacaktır, ancak daha sonra basit nesne başlatıcı sözdizimini kaybedersiniz.


4

Düz JavaScript Nesneleri yerine Haritalar ne zaman kullanılır?

Düz JavaScript Nesnesi {key: 'value'} yapılandırılmış verileri tutar. Ancak düz JS nesnesinin sınırlamaları vardır:

  1. Nesnelerin anahtarı olarak yalnızca dizeler ve semboller kullanılabilir. Başka bir şey kullanırsak, sayılar bir nesnenin anahtarları olarak kullanılırsa, o zaman bu anahtarlara erişilirken, bu anahtarların türlerin tutarlılığını kaybetmemize neden olan dolaylı olarak dizelere dönüştürüleceğini göreceğiz. const names = {1: 'bir', 2: 'iki'}; Object.keys (isim); // ['1', '2']

  2. JS tanımlayıcılarını bir nesnenin anahtar adları (örn. ToString, yapıcı vb.) Olarak yazarak prototiplerden yanlışlıkla devralınan özelliklerin üzerine yazma olasılığı vardır.

  3. Başka bir nesne bir nesnenin anahtarı olarak kullanılamaz, bu nedenle o nesneyi başka bir nesnenin anahtarı olarak yazarak hiçbir ekstra bilgi yazılamaz ve başka bir nesnenin değeri fazladan bilgi içerecektir

  4. Nesneler yineleyici değildir

  5. Bir nesnenin boyutu doğrudan belirlenemez

Nesnelerin bu sınırlamaları Haritalar tarafından çözülür, ancak Haritalar'ı değiştirme yerine Nesneler için bir tamamlayıcı olarak düşünmeliyiz. Temelde Harita sadece diziler dizisidir, ancak bu dizi dizisini Map nesnesine yeni anahtar sözcükle bağımsız değişken olarak iletmeliyiz, aksi takdirde yalnızca dizi dizisi için Map'in kullanışlı özellikleri ve yöntemleri kullanılamaz. Ayrıca, dizi dizisindeki anahtar / değer çiftlerini veya Harita'nın yalnızca virgülle ayrılması gerektiğini unutmayın; düz nesnelerde olduğu gibi iki nokta üst üste işareti bulunmamalıdır.

Harita veya Nesne kullanmaya karar vermek için 3 ipucu:

  1. Anahtarlar çalışma süresine kadar bilinmeyen nesneler üzerinde eşlemeler kullanın, çünkü kullanıcı girişi tarafından oluşturulan veya bilmeden bu anahtarlar nesnenin devralınan özelliklerinin üzerine yazılırsa nesneyi kullanan kodu kırabilir, bu nedenle bu durumlarda harita daha güvenlidir. Ayrıca tüm anahtarlar aynı türdeyse ve tüm haritalar aynı türdeyse haritaları kullanın.

  2. İlkel değerleri anahtar olarak saklamanız gerektiğinde haritaları kullanın.

  3. Tek tek elemanlar üzerinde çalışmamız gerekiyorsa nesneleri kullanın.

Google Haritalar kullanmanın yararları şunlardır:

1. Harita herhangi bir anahtar türünü kabul eder ve anahtar türünü korur:

Nesnenin anahtarı bir dize veya simge değilse JS'nin bunu dolaylı olarak bir dizeye dönüştürdüğünü biliyoruz. Aksine, Harita her tür anahtarı kabul eder: dize, sayı, boole, sembol vb. Ve Harita orijinal anahtar türünü korur. Burada sayıyı bir Harita içinde anahtar olarak kullanacağız ve sayı olarak kalacaktır:

const numbersMap= new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

const keysOfMap= [...numbersMap.keys()];

console.log(keysOfMap);                        // [1, 2]

Bir Harita içinde tüm nesneyi anahtar olarak bile kullanabiliriz. Yalın nesnelerle çalışabilmemiz, ancak nesne hakkında bazı bilgileri saklayabilmemiz için bu verileri nesnenin içine eklemeden bazı nesne ile ilgili verileri saklamak istediğimiz zamanlar olabilir. Bu durumlarda, Nesneyi anahtar olarak ve nesnenin ilgili verilerini değer olarak yapabilmemiz için Harita'yı kullanmamız gerekir.

const foo= {name: foo};

const bar= {name: bar};

const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];

Ancak bu yaklaşımın dezavantajı, değere anahtarla erişmenin karmaşıklığıdır, çünkü istenen değeri elde etmek için tüm dizi boyunca döngü yapmamız gerekir.

function getBy Key(kindOfMap, key) {
    for (const [k, v]  of kindOfMap) {
        if(key === k) {
            return v;
        }
    }
    return undefined;
}

getByKey(kindOfMap, foo);            // 'Foo related data'

Bu değere doğrudan erişememe sorununu uygun bir Harita kullanarak çözebiliriz.

const foo= {name: 'foo'};

const bar= {name: 'bar'};

const myMap= new Map();

myMap.set(foo, 'Foo related data');
myMap.set(bar, 'Bar related data');

console.log(myMap.get(foo));            // 'Foo related data'

Biz bunu WeakMap kullanarak yapmış olabilir, sadece yazmak zorunda, const myMap = new WeakMap (). Harita ve WeakMap arasındaki farklar, WeakMap'in anahtarların çöp toplanmasına izin vermesidir (burada nesneler), böylece bellek sızıntılarını önler, WeakMap yalnızca nesneleri anahtar olarak kabul eder ve WeakMap'in yöntem kümesini azaltmasıdır.

2. Haritanın anahtar adları üzerinde herhangi bir kısıtlaması yoktur:

Düz JS nesneleri için, prototipten miras alınan özelliğin üzerine yanlışlıkla yazabiliriz ve tehlikeli olabilir. Burada actor nesnesinin toString () özelliğinin üzerine yazacağız:

const actor= {
    name: 'Harrison Ford',
    toString: 'Actor: Harrison Ford'
};

Şimdi, sağlanan bağımsız değişkenin düz bir nesne olup olmadığını belirlemek için bir fn isPlainObject () tanımlayalım ve bu fn, denetlemek için toString () yöntemini kullanır:

function isPlainObject(value) {
    return value.toString() === '[object Object]';
}

isPlainObject(actor);        // TypeError : value.toString is not a function

// this is because inside actor object toString property is a string instead of inherited method from prototype

Haritanın anahtar adları üzerinde herhangi bir kısıtlaması yoktur, toString, yapıcı vb.

const actorMap= new Map();

actorMap.set('name', 'Harrison Ford');

actorMap.set('toString', 'Actor: Harrison Ford');

function isMap(value) {
  return value.toString() === '[object Map]';
}

console.log(isMap(actorMap));     // true

Kullanıcı girişinin anahtarlar oluşturduğu bir durumumuz varsa, bu anahtarları düz bir nesne yerine bir Harita içine almalıyız. Bunun nedeni, kullanıcının toString, yapıcı vb.Gibi özel bir alan adı seçebilmesidir, bu durumda düz bir nesnede bu tür anahtar adları, daha sonra bu nesneyi kullanan kodu kırabilir. Bu nedenle doğru çözüm, kullanıcı arayüzü durumunu bir haritaya bağlamaktır, Haritayı kırmanın bir yolu yoktur:

const userCustomFieldsMap= new Map([['color', 'blue'], ['size', 'medium'], ['toString', 'A blue box']]);

3. Harita yinelenebilir:

Düz bir nesnenin özelliklerini yinelemek için Object.entries () veya Object.keys () gerekir. Object.entries (plainObject), nesneden ayıklanan bir dizi anahtar değer çifti döndürür, daha sonra bu anahtarları ve değerleri yapılandırabilir ve normal anahtarlar ve değerler çıktısı alabiliriz.

const colorHex= {
  'white': '#FFFFFF',
  'black': '#000000'
}

for(const [color, hex] of Object.entries(colorHex)) {
  console.log(color, hex);
}
//
'white' '#FFFFFF'   
'black' '#000000'

Haritalar yinelenebilir olduğundan, bir Harita üzerinde yineleme ve anahtarın yok edilmesi için girişler () yöntemlerine ihtiyacımız yoktur, değer dizisi bir Harita'nın içinde olduğu gibi doğrudan Harita üzerinde yapılabilir. .

const colorHexMap= new Map();
colorHexMap.set('white', '#FFFFFF');
colorHexMap.set('black', '#000000');


for(const [color, hex] of colorHexMap) {
  console.log(color, hex);
}
//'white' '#FFFFFF'   'black' '#000000'

Ayrıca map.keys (), anahtarlar üzerinden yineleyici ve map.values ​​() değerler üzerinde bir yineleyici döndürür.

4. Bir haritanın boyutunu kolayca bilebiliriz

Düz bir nesnedeki özellik sayısını doğrudan belirleyemiyoruz. Nesnenin anahtarları ile bir dizi döndüren Object.keys () gibi fn gibi bir yardımcıya ihtiyacımız var, sonra length özelliğini kullanarak anahtar sayısını veya düz nesnenin boyutunu elde edebiliriz.

const exams= {'John Rambo': '80%', 'James Bond': '60%'};

const sizeOfObj= Object.keys(exams).length;

console.log(sizeOfObj);       // 2

Ancak Haritalar söz konusu olduğunda map.size özelliğini kullanarak Harita boyutuna doğrudan erişebiliriz.

const examsMap= new Map([['John Rambo', '80%'], ['James Bond', '60%']]);

console.log(examsMap.size);

1

Bu iki ipucu, Harita veya Nesne kullanmaya karar vermenize yardımcı olabilir:

  • Anahtarlar çalışma süresine kadar bilinmiyorsa ve tüm anahtarlar aynı türdeyse ve tüm değerler aynı türdeyse, nesneler üzerinde eşlemeler kullanın.

  • İlkel değerleri anahtar olarak depolamaya ihtiyaç duyulması durumunda haritaları kullanın, çünkü nesne her bir anahtarı dize olarak sayı değeri, boole değeri veya başka bir ilkel değer olarak görür.

  • Tek tek öğeler üzerinde çalışan bir mantık olduğunda nesneleri kullanın.

Kaynak: https://developer.mozilla.org/tr-TR/docs/Web/JavaScript/Guide/Keyed_Collections#Object_and_Map_compared


2
Bu ipuçları, özellikle işleri bu kriterlere göre bölümlere ayırmak kolay olmadığı için özellikle yararlı görünmemektedir. Anahtarlar / değerler aynı tipte olduğunda haritalar bir yarar neden ilk ile alamadım. Sınıflar / yapılar gibi nesneler, koleksiyonlar gibi haritalar kullanmayı söylemeye çalışıyor gibi görünüyor. İkincisi, noktaya ulaşmamak için kötü yazılmıştır. Gerçekten karışık dize eşdeğeri türleriniz ("1" ve 1) olduğunda veya anahtar türlerini korumak istediğinizde / kullanmak istediğinizde haritalar kullanmak anlamına gelir. Birincisi ile aynı olduğunu düşündüğüm sonuncusu, bir nesnenin ne olduğunu bilmediğinizi varsayıyor, bu yüzden belirsiz.
jgmjgm

1

Bu hatırlamamın kısa bir yolu: KOI

  1. Anahtarlar. Nesne anahtarı dizeler veya sembollerdir. Harita tuşları aynı zamanda sayılar (1 ve "1" farklıdır), nesneler NaN, vb. Olabilir. Tek ===bir istisna dışında tuşları ayırt etmek NaN !== NaNiçin kullanılır,NaN bir anahtar olarak.
  2. Sipariş. Kampanya siparişi hatırlanır. Yani [...map]veya[...map.keys()] da belirli bir emri var.
  3. Arayüz. Nesne: obj[key]veya obj.a(bazı dillerde []ve []=gerçekten arayüzün bir parçasıdır). Harita sahiptir get(), set(), has(), delete()kendinizin kullanabileceği vb Not map[123]ama düz JS nesne olarak kullandığını öne sürdü.

0

Mozilla'ya göre

Örnekler ile kısa sürede JavaScript'te nesne vs Harita .

Nesne- harita ile aynı kavramı takip eder, yani veri depolamak için anahtar / değer çifti kullanmak. Ancak haritayı belirli durumlarda daha iyi bir performans yapan küçük farklılıklar vardır.

Harita- Verilerin çiftler halinde saklanmasına yardımcı olan bir veri yapısıdır. Çift, benzersiz bir anahtar ve anahtarla eşlenen bir değerden oluşur. Yinelemeyi önlemeye yardımcı olur.

Temel farklılıklar

  • Harita bir nesnenin örneğidir, ancak tam tersi doğru değildir.

var map = new Map();
var obj = new Object(); 
console.log(obj instanceof Map);   // false
console.log(map instanceof Object);  // true

  • Object uygulamasında, anahtar alanının veri türü tamsayı, dizeler ve sembollerle sınırlıdır. Harita'da, anahtar alan herhangi bir veri türünde (tamsayı, bir dizi, bir nesne) olabilir

var map = new Map();//Empty 
map.set(1,'1');
map.set('one', 1);
map.set('{}', {name:'Hello world'});
map.set(12.3, 12.3)
map.set([12],[12345])

for(let [key,value] of map.entries())
  console.log(key+'---'+value)

  • Haritada, öğelerin orijinal sırası korunur. Nesneler için bu doğru değildir.

let obj ={
  1:'1',
  'one':1,
  '{}': {name:'Hello world'},
  12.3:12.3,
  [12]:[100]
}
console.log(obj)


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.