Peta vs Objek dalam JavaScript

290

Saya baru saja menemukan chromestatus.com dan, setelah kehilangan beberapa jam dalam sehari, menemukan entri fitur ini :

Peta: Objek peta adalah peta kunci / nilai sederhana.

Itu membingungkan saya. Objek JavaScript biasa adalah kamus, jadi apa Mapbedanya dengan kamus? Secara konseptual, mereka identik (menurut Apa perbedaan antara Peta dan Kamus? )

Referensi dokumentasi chromestatus juga tidak membantu:

Objek peta adalah koleksi pasangan kunci / nilai di mana kunci dan nilai dapat berupa nilai bahasa skrip ECMAS. Nilai kunci yang berbeda hanya dapat terjadi dalam satu pasangan kunci / nilai dalam koleksi Peta. Nilai kunci yang berbeda didiskriminasi menggunakan algoritma perbandingan yang dipilih saat Peta dibuat.

Objek Peta dapat mengulangi elemen-elemennya dalam urutan penyisipan. Objek peta harus diimplementasikan menggunakan tabel hash atau mekanisme lain yang, rata-rata, menyediakan waktu akses yang sublinear pada jumlah elemen dalam koleksi. Struktur data yang digunakan dalam spesifikasi objek Peta ini hanya dimaksudkan untuk menggambarkan semantik yang dapat diamati dari objek Peta. Ini tidak dimaksudkan sebagai model implementasi yang layak.

... masih terdengar seperti objek bagiku, jadi jelas aku telah melewatkan sesuatu.

Mengapa JavaScript mendapatkan objek (yang didukung) Map? Apa fungsinya?

Dave
sumber

Jawaban:

286

Menurut mozilla:

Objek Peta dapat mengulangi elemen-elemennya dalam urutan penyisipan - a for..of loop akan mengembalikan array [kunci, nilai] untuk setiap iterasi.

dan

Objek serupa dengan Peta yang memungkinkan Anda mengatur kunci ke nilai, mengambil nilai-nilai itu, menghapus kunci, dan mendeteksi apakah ada sesuatu yang disimpan pada kunci. Karena itu, Objek telah digunakan sebagai Maps secara historis; namun, ada perbedaan penting antara Objek dan Peta yang menjadikan penggunaan Peta menjadi lebih baik.

Objek memiliki prototipe, jadi ada kunci default di peta. Namun, ini dapat dilewati menggunakan map = Object.create (null). Kunci dari suatu Objek adalah String, di mana mereka dapat menjadi nilai apa pun untuk Peta. Anda bisa mendapatkan ukuran Peta dengan mudah sementara Anda harus secara manual melacak ukuran untuk Obyek.

Gunakan peta di atas objek ketika kunci tidak diketahui sampai waktu berjalan, dan ketika semua kunci adalah tipe yang sama dan semua nilai adalah tipe yang sama.

Gunakan objek ketika ada logika yang beroperasi pada elemen individual.

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

Iterability-in-order adalah fitur yang telah lama diinginkan oleh pengembang, sebagian karena memastikan kinerja yang sama di semua browser. Jadi bagi saya itu yang besar.

The myMap.has(key)Metode akan sangat berguna, dan juga myMap.sizeproperti.


sumber
13
Kelemahannya, mungkin, adalah bahwa Peta membutuhkan lebih banyak memori (dalam urutan besarnya yang sama, namun) untuk mempertahankan urutan penyisipan.
John Kurlak
4
Peta memiliki fitur lain selain keteraturan yang telah disebutkan di sini (menggunakan objek apa pun sebagai kunci, pemisahan kunci dan alat peraga, dll.), Tetapi FWIW dalam beberapa kasus urutan iterasi properti objek biasa didefinisikan oleh ES2015. Lihat stackoverflow.com/a/32149345 .
JMM
2
Saya tidak mendapatkan artinya, ketika Anda mengatakan, Sebuah Objek memiliki prototipe, jadi ada kunci default di peta. Namun, ini dapat dilewati menggunakanmap = Object.create(null) . Apa itu kunci default? Bagaimana kunci terkait Object.prototype?
pertukaran berlebihan
4
Pengujian saya di Chrome menunjukkan bahwa peta tidak menggunakan memori lebih banyak dalam jumlah signifikan untuk menjaga ketertiban. Saya kira ada 0,1KB lebih untuk satu juta kunci dan saya tidak berpikir itu untuk menjaga ketertiban. Namun, itu ~ 0,1KB tampaknya menjadi overhead konstan. Jika Anda membuat sejuta peta dengan satu tombol saja dan membandingkannya jauh lebih besar dari objek.
jgmjgm
2
@luxon Anda membuat objek di sana. ES6 spec mengharuskan newoperator untuk digunakan dengan Mapsimbol yaitu new Mapuntuk membuat objek peta. var a = {}adalah singkatan untuk (artinya setara dengan)var a = Object.create(Object.prototype)
dudewad
104

Perbedaan utama adalah bahwa Objek hanya mendukung kunci string di mana Peta mendukung lebih atau kurang jenis kunci apa pun.

Jika saya melakukan obj[123] = truedan kemudian Object.keys(obj)kemudian saya akan mendapatkan ["123"]bukan [123]. Peta akan mempertahankan jenis kunci dan mengembalikan [123]yang bagus. Peta juga memungkinkan Anda menggunakan Objek sebagai kunci. Secara tradisional untuk melakukan ini, Anda harus memberikan objek semacam pengidentifikasi unik untuk hash mereka (saya tidak berpikir saya pernah melihat sesuatu seperti getObjectIddi JS sebagai bagian dari standar). Peta juga menjamin pelestarian pesanan sehingga semuanya lebih baik untuk pelestarian dan kadang-kadang dapat menghemat Anda perlu melakukan beberapa jenis.

Antara peta dan objek dalam praktiknya ada beberapa pro dan kontra. Objek mendapatkan keuntungan dan kerugian terintegrasi dengan sangat ketat ke dalam inti JavaScript yang membedakan mereka dari Peta yang signifikan di luar perbedaan dalam dukungan kunci.

Keuntungan langsung adalah bahwa Anda memiliki dukungan sintaksis untuk Objek sehingga memudahkan untuk mengakses elemen. Anda juga memiliki dukungan langsung untuk itu dengan JSON. Ketika digunakan sebagai hash itu menjengkelkan untuk mendapatkan objek tanpa properti sama sekali. Secara default jika Anda ingin menggunakan Objects sebagai tabel hash mereka akan tercemar dan Anda sering harus memanggil hasOwnPropertymereka ketika mengakses properti. Anda dapat melihat di sini bagaimana secara default Objek terpolusi dan cara membuat objek yang diharapkan tidak terpolusi untuk digunakan sebagai hash:

({}).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

Polusi pada objek bukan hanya sesuatu yang membuat kode lebih mengganggu, lebih lambat, dll tetapi juga dapat memiliki konsekuensi potensial untuk keamanan.

Objek bukan tabel hash murni tetapi berusaha melakukan lebih banyak. Anda memiliki sakit kepala seperti hasOwnProperty, tidak bisa mendapatkan panjangnya dengan mudah ( Object.keys(obj).length) dan sebagainya. Objek tidak dimaksudkan untuk semata-mata digunakan sebagai peta hash tetapi sebagai objek yang dapat diperluas dinamis juga dan saat Anda menggunakannya sebagai tabel hash murni muncul masalah.

Perbandingan / Daftar berbagai operasi umum:

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

Ada beberapa opsi lain, pendekatan, metodologi, dll dengan berbagai pasang surut (kinerja, singkat, portabel, dapat diperpanjang, dll). Objek agak aneh karena menjadi inti bahasa sehingga Anda memiliki banyak metode statis untuk bekerja dengannya.

Selain keuntungan dari Maps mempertahankan jenis kunci serta mampu mendukung hal-hal seperti objek sebagai kunci mereka diisolasi dari efek samping yang dimiliki banyak objek. Peta adalah hash murni, tidak ada kebingungan tentang mencoba menjadi objek pada saat yang sama. Peta juga dapat dengan mudah diperluas dengan fungsi proxy. Objek saat ini memiliki kelas Proxy namun kinerja dan penggunaan memori suram, sebenarnya membuat proxy Anda sendiri yang tampak seperti Map for Objects saat ini berkinerja lebih baik daripada Proxy.

Kerugian substansial untuk Maps adalah bahwa mereka tidak didukung dengan JSON secara langsung. Parsing dimungkinkan tetapi memiliki beberapa hangup:

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

Di atas akan memperkenalkan hit kinerja serius dan juga tidak akan mendukung kunci string apa pun. Pengkodean JSON bahkan lebih sulit dan bermasalah (ini adalah salah satu dari banyak pendekatan):

// 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())
    });
};

Ini tidak terlalu buruk jika Anda murni menggunakan Maps tetapi akan memiliki masalah ketika Anda mencampur jenis atau menggunakan nilai-nilai non-skalar sebagai kunci (bukan bahwa JSON sempurna dengan masalah seperti itu, referensi objek melingkar IE). Saya belum mengujinya, tetapi kemungkinan besar itu akan sangat merusak kinerja dibandingkan dengan menjernihkan.

Bahasa scripting lain sering tidak memiliki masalah seperti itu karena mereka memiliki jenis non-skalar eksplisit untuk Peta, Objek dan Array. Pengembangan web seringkali merepotkan dengan jenis non-skalar di mana Anda harus berurusan dengan hal-hal seperti PHP menggabungkan Array / Peta dengan Objek menggunakan A / M untuk properti dan JS menggabungkan Peta / Objek dengan Array memperluas M / O. Menggabungkan tipe kompleks adalah kutukan iblis dari bahasa skrip tingkat tinggi.

Sejauh ini sebagian besar masalah seputar implementasi tetapi kinerja untuk operasi dasar juga penting. Performanya juga kompleks karena tergantung pada mesin dan penggunaan. Ikuti tes saya dengan sebutir garam karena saya tidak bisa mengesampingkan kesalahan (saya harus buru-buru). Anda juga harus menjalankan tes Anda sendiri untuk mengonfirmasi karena saya hanya memeriksa skenario sederhana yang sangat spesifik untuk memberikan indikasi kasar saja. Menurut pengujian di Chrome untuk objek / peta yang sangat besar, kinerja untuk objek lebih buruk karena penghapusan yang tampaknya sebanding dengan jumlah kunci daripada O (1):

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 jelas memiliki keuntungan yang kuat dengan mendapatkan dan memperbarui tetapi kinerja penghapusannya mengerikan. Peta menggunakan sedikit memori lebih banyak dalam hal ini (overhead) tetapi dengan hanya satu Objek / Peta yang diuji dengan jutaan tombol, dampak overhead untuk peta tidak dinyatakan dengan baik. Dengan objek manajemen memori juga tampaknya gratis sebelumnya jika saya membaca profil dengan benar yang mungkin menjadi salah satu manfaat dari objek.

Di Firefox untuk tolok ukur khusus ini, ini adalah cerita yang berbeda:

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

Saya harus segera menunjukkan bahwa dalam benchmark khusus ini menghapus dari objek di Firefox tidak menyebabkan masalah, namun di tolok ukur lain telah menyebabkan masalah terutama ketika ada banyak kunci seperti di Chrome. Peta jelas lebih unggul di Firefox untuk koleksi besar.

Namun ini bukan akhir dari cerita, bagaimana dengan banyak benda kecil atau peta? Saya telah melakukan patokan cepat ini tetapi tidak lengkap (pengaturan / mendapatkan) yang berkinerja terbaik dengan sejumlah kecil kunci dalam operasi di atas. Tes ini lebih lanjut tentang memori dan inisialisasi.

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

Sekali lagi angka-angka ini bervariasi tetapi pada dasarnya Obyek memiliki petunjuk yang baik. Dalam beberapa kasus, petunjuk untuk Objects over maps adalah ekstrim (~ 10 kali lebih baik) tetapi rata-rata sekitar 2-3 kali lebih baik. Tampaknya lonjakan kinerja yang ekstrem dapat bekerja dengan dua arah. Saya hanya menguji ini di Chrome dan membuat ke penggunaan memori profil dan overhead. Saya cukup terkejut melihat bahwa di Chrome tampak bahwa Maps dengan satu tombol menggunakan sekitar 30 kali lebih banyak memori daripada Objects dengan satu kunci.

Untuk menguji banyak objek kecil dengan semua operasi di atas (4 kunci):

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

Dalam hal alokasi memori, ini berperilaku sama dalam hal membebaskan / GC tetapi Peta menggunakan memori 5 kali lebih banyak. Tes ini menggunakan 4 kunci di mana seperti dalam tes terakhir saya hanya menetapkan satu kunci sehingga ini akan menjelaskan pengurangan overhead memori. Saya menjalankan tes ini beberapa kali dan Peta / Obyek kurang lebih secara keseluruhan untuk Chrome dalam hal kecepatan keseluruhan. Di Firefox untuk Objek kecil ada keunggulan kinerja yang pasti atas peta secara keseluruhan.

Ini tentu saja tidak termasuk opsi individu yang bisa sangat bervariasi. Saya tidak akan menyarankan pengoptimalan secara mikro dengan angka-angka ini. Apa yang dapat Anda peroleh dari hal ini adalah sebagai patokan, pertimbangkan Peta lebih kuat untuk penyimpanan nilai kunci yang sangat besar dan objek untuk penyimpanan nilai kunci kecil.

Di luar itu strategi terbaik dengan keduanya adalah mengimplementasikannya dan hanya membuatnya bekerja terlebih dahulu. Ketika memprofilkan, penting untuk diingat bahwa kadang-kadang hal-hal yang tidak Anda pikir akan lambat ketika melihatnya bisa sangat lambat karena kebiasaan mesin seperti yang terlihat pada kasus penghapusan kunci objek.

jgmjgm
sumber
Kurang serializability telah menjadi masalah nyata bagi banyak pengembang. Lihatlah upvote dari Bagaimana saya bertahan Peta ES6 di penyimpanan lokal (atau di tempat lain)? dan Bagaimana Anda JSON.stringify ES6 Map? .
Franklin Yu
Apakah angka dalam milidetik, byte atau objek total?
StefansArya
Mengambil begitu ms (butuh waktu singkat untuk mengatakan sesuatu yang digunakan, sehingga menghabiskan waktu dalam kasus ini). Meskipun ini adalah tes lama dan saya tidak memiliki kode tolok ukur lagi. Mungkin sangat berbeda sekarang. Masalah hapus misalnya saya yakin sudah diperbaiki.
jgmjgm
27

Saya tidak berpikir poin-poin berikut telah disebutkan dalam jawaban sejauh ini, dan saya pikir mereka akan layak disebutkan.


Peta bisa lebih besar

Di chrome saya bisa mendapatkan 16,7 juta pasangan kunci / nilai dengan Mapvs 11,1 juta dengan objek biasa. Hampir tepatnya 50% lebih banyak pasangan dengan Map. Mereka berdua menghabiskan sekitar 2GB memori sebelum crash, dan jadi saya pikir mungkin berkaitan dengan pembatasan memori dengan chrome ( Edit : Yap, coba isi 2 Mapsdan Anda hanya mendapatkan masing-masing 8,3 juta pasang sebelum crash). Anda dapat mengujinya sendiri dengan kode ini (jalankan secara terpisah dan tidak pada saat yang bersamaan, jelas):

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

Objek sudah memiliki beberapa properti / kunci

Yang ini telah membuatku tersandung sebelumnya. Benda biasa memiliki toString, constructor, valueOf, hasOwnProperty, isPrototypeOfdan sekelompok yang sudah ada sifat-sifat lainnya. Ini mungkin bukan masalah besar bagi sebagian besar kasus penggunaan, tetapi telah menyebabkan masalah bagi saya sebelumnya.

Peta bisa lebih lambat:

Karena .getoverhead panggilan fungsi dan kurangnya optimalisasi internal, Peta dapat jauh lebih lambat daripada objek JavaScript biasa untuk beberapa tugas.


sumber
1
Menurut Anda, apakah semantik lebih penting daripada kinerja di sini? Peta terdengar sempurna jika Anda membutuhkan kamus, tetapi sulit untuk menerima pencarian yang lebih lambat. Bukankah pencarian cepat adalah inti dari kamus?
user2954463
3
Saya pasti akan pergi dengan benda-benda tua biasa jika Anda sedang baik-baik saja dengan 11 juta pasangan kunci / nilai dan tidak peduli tentang tombol yang sudah ada seperti toString, constructor, dll (yaitu kunci Anda sangat tidak mungkin bertabrakan dengan mereka). Mereka lebih mudah untuk dikerjakan - misalnya kenaikan adalah obj[i] = (obj[i] || 0) + 1, sedangkan dengan Mapitu map.set(i, (map.get(i) || 0) + 1)yang masih tidak terlalu buruk, tetapi itu hanya menunjukkan bagaimana hal-hal bisa menjadi berantakan tanpa perlu. Peta pasti memiliki kasus penggunaannya, tetapi seringkali objek sederhana akan melakukannya.
Perhatikan bahwa Anda dapat menyingkirkan default toString, constructor, (dll) properti obyek dengan menulis obj = Object.create(null)bukan obj = {}.
17

Objek dapat berperilaku seperti kamus karena Javascript diketik secara dinamis, memungkinkan Anda untuk menambah atau menghapus properti pada suatu objek kapan saja.

Tetapi Map()fungsi baru ini jauh lebih baik karena:

  • Ini menyediakan get, set, has, dan deletemetode.
  • Terima jenis apa pun untuk kunci, bukan hanya string.
  • Menyediakan iterator untuk for-ofpenggunaan yang mudah dan menjaga urutan hasil.
  • Tidak memiliki casing tepi dengan prototipe dan properti lainnya yang muncul selama iterasi atau penyalinan.
  • Ini mendukung jutaan item.
  • Sangat cepat dan terus semakin cepat karena mesin javascript menjadi lebih baik.

99% dari waktu Anda hanya harus menggunakan a Map(). Namun, jika Anda hanya menggunakan kunci berbasis string dan membutuhkan kinerja baca maksimal, maka objek bisa menjadi pilihan yang lebih baik.

Detailnya adalah bahwa (hampir semua) mesin javascript mengkompilasi objek ke kelas C ++ di latar belakang. Tipe-tipe ini di-cache dan digunakan kembali oleh "outline" mereka, jadi ketika Anda membuat objek baru dengan properti yang persis sama, mesin akan menggunakan kembali kelas latar belakang yang ada. Jalur akses untuk properti di kelas-kelas ini sangat optimal dan jauh lebih cepat daripada pencarian a Map().

Menambah atau menghapus properti menyebabkan kelas cadangan di-cache untuk dikompilasi ulang yang mengapa menggunakan objek sebagai kamus dengan banyak penambahan dan penghapusan kunci sangat lambat, tetapi membaca dan menetapkan kunci yang ada tanpa mengubah objek sangat cepat.

Jadi, jika Anda memiliki beban kerja tulis-sekali-baca-berat dengan kunci tombol maka gunakan objectkamus khusus berkinerja tinggi, tetapi untuk yang lainnya gunakan a Map().

Mani Gandham
sumber
Objek menyediakan get set has deletefungsionalitas dll juga, hanya saja tidak begitu elegan (tapi tidak buruk juga). Dengan cara apa Maplebih mudah digunakan untuk iterasi? Tidak yakin saya bisa setuju.
Andrew
@Andrew Saya mengambil tentang metode, dan fungsi berbeda juga tergantung pada apa yang Anda gunakan dan hasilnya. Iterasi lebih mudah karena prototipe dan properti asli tidak muncul dalam loop dan menggunakan iterator JS normal yang mempertahankan urutan yang sama.
Mani Gandham
11

Selain jawaban yang lain, saya telah menemukan bahwa Maps lebih berat dan verbose untuk dioperasikan daripada objek.

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

Ini penting, karena kode yang lebih pendek lebih cepat dibaca, lebih ekspresif secara langsung, dan lebih baik disimpan di kepala programmer .

Aspek lain: karena set () mengembalikan peta, bukan nilai, tidak mungkin untuk rantai tugas.

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

Peta debugging juga lebih menyakitkan. Di bawah, Anda tidak dapat benar-benar melihat kunci apa yang ada di peta. Anda harus menulis kode untuk melakukan itu.

Selamat mencoba mengevaluasi Map Iterator

Objek dapat dievaluasi oleh IDE apa pun:

WebStorm mengevaluasi suatu objek

Dan Dascalescu
sumber
4
Mengingat semua ini, sepertinya peta adalah optimasi prematur.
PRMan
10

Ringkasan:

  • Object: Struktur data tempat data disimpan sebagai pasangan nilai kunci. Dalam suatu objek kuncinya harus berupa angka, string, atau simbol. Nilai dapat berupa apa saja, begitu juga objek lain, fungsi, dll. Objek adalah struktur data yang tidak diurutkan , yaitu urutan penyisipan pasangan nilai kunci tidak diingat
  • ES6 Map: Struktur data tempat data disimpan sebagai pasangan nilai kunci. Di mana kunci unik memetakan suatu nilai . Baik kunci maupun nilainya bisa dalam tipe data apa pun . Peta adalah struktur data yang dapat diulang, ini berarti bahwa urutan penyisipan diingat dan bahwa kita dapat mengakses elemen-elemen dalam misal sebuah for..ofloop

Perbedaan utama:

  • A Mapdipesan dan dapat diubah, sedangkan objek tidak diurutkan dan tidak dapat diubah

  • Kita dapat menempatkan semua jenis data sebagai Mapkunci, sedangkan objek hanya dapat memiliki angka, string, atau simbol sebagai kunci.

  • A Mapmewarisi dari Map.prototype. Ini menawarkan segala macam fungsi utilitas dan properti yang membuat bekerja dengan Mapobjek jauh lebih mudah.

Contoh:

obyek:

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)

Peta:

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

sumber: MDN

Willem van der Veen
sumber
4

Selain itu agar dapat diulang dalam urutan yang jelas, dan kemampuan untuk menggunakan nilai arbitrer sebagai kunci (kecuali -0), peta bisa bermanfaat karena alasan berikut:

  • Spesifikasi ini memberlakukan operasi peta rata-rata menjadi sublinear.

    Implementasi objek yang tidak bodoh akan menggunakan tabel hash atau yang serupa, jadi pencarian properti mungkin akan konstan rata-rata. Maka objek bisa lebih cepat dari peta. Tapi itu tidak diperlukan oleh spec.

  • Objek dapat memiliki perilaku tak terduga yang tidak menyenangkan.

    Misalnya, katakanlah Anda tidak menyetel fooproperti apa pun ke objek yang baru dibuat obj, jadi Anda berharap obj.foountuk mengembalikan yang tidak ditentukan. Tapi foobisa jadi properti bawaan diwarisi dari Object.prototype. Atau Anda mencoba membuat obj.foodengan menggunakan tugas, tetapi beberapa setter dalam Object.prototypemenjalankan alih-alih menyimpan nilai Anda.

    Peta mencegah hal-hal semacam ini. Ya, kecuali beberapa skrip mengacaukan Map.prototype. Dan Object.create(null)akan bekerja juga, tetapi kemudian Anda kehilangan sintaks penginisialisasi objek sederhana.

Oriol
sumber
4

Kapan harus menggunakan Peta, bukan Objek JavaScript biasa?

Obyek JavaScript sederhana {key: 'value'} menyimpan data terstruktur. Tetapi objek JS sederhana memiliki keterbatasan:

  1. Hanya string dan simbol yang dapat digunakan sebagai kunci dari Objek. Jika kita menggunakan hal-hal lain mengatakan, angka sebagai kunci dari suatu objek maka saat mengakses kunci tersebut kita akan melihat kunci-kunci itu akan dikonversi menjadi string yang secara implisit menyebabkan kita kehilangan konsistensi jenis. const names = {1: 'one', 2: 'two'}; Object.keys (nama); // ['1', '2']

  2. Ada kemungkinan secara tidak sengaja menimpa properti yang diwarisi dari prototipe dengan menulis pengidentifikasi JS sebagai nama kunci dari suatu objek (misalnya toString, konstruktor, dll.)

  3. Objek lain tidak dapat digunakan sebagai kunci dari suatu objek, jadi tidak ada informasi tambahan yang dapat ditulis untuk suatu objek dengan menulis objek itu sebagai kunci dari objek lain dan nilai objek lain itu akan berisi informasi tambahan

  4. Objek bukan iterator

  5. Ukuran suatu objek tidak dapat ditentukan secara langsung

Batasan-batasan Objek ini diselesaikan oleh Peta tetapi kita harus mempertimbangkan Peta sebagai pelengkap untuk Objek alih-alih penggantian. Pada dasarnya Map hanyalah array array tetapi kita harus meneruskan array array tersebut ke objek Map sebagai argumen dengan kata kunci baru, jika tidak hanya untuk array array, properti dan metode Map yang berguna tidak tersedia. Dan ingat pasangan kunci-nilai di dalam array array atau Peta harus dipisahkan dengan koma saja, tidak ada titik dua seperti pada objek biasa.

3 tips untuk memutuskan apakah akan menggunakan Peta atau Objek:

  1. Gunakan peta di atas objek ketika kunci tidak diketahui hingga waktu berjalan karena kunci yang dibentuk oleh input pengguna atau tanpa sadar dapat memecahkan kode yang menggunakan objek jika kunci tersebut menimpa properti yang diwarisi dari objek, sehingga peta lebih aman dalam kasus tersebut. Juga gunakan peta ketika semua tombol adalah tipe yang sama dan semua peta adalah tipe yang sama.

  2. Gunakan peta jika ada kebutuhan untuk menyimpan nilai-nilai primitif sebagai kunci.

  3. Gunakan objek jika kita perlu beroperasi pada elemen individual.

Manfaat menggunakan Maps adalah:

1. Peta menerima semua jenis kunci dan mempertahankan jenis kunci:

Kita tahu bahwa jika kunci objek bukan string atau simbol maka JS secara implisit mengubahnya menjadi string. Sebaliknya, Map menerima semua jenis kunci: string, angka, boolean, simbol dll. Dan Map mempertahankan jenis kunci asli. Di sini kita akan menggunakan angka sebagai kunci di dalam Peta dan nomor itu akan tetap:

const numbersMap= new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

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

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

Di dalam Peta kita bahkan bisa menggunakan seluruh objek sebagai kunci. Mungkin ada saat-saat ketika kita ingin menyimpan beberapa data terkait objek, tanpa melampirkan data ini di dalam objek itu sendiri sehingga kita dapat bekerja dengan objek lean tetapi ingin menyimpan beberapa informasi tentang objek. Dalam kasus-kasus tersebut kita perlu menggunakan Peta sehingga kita dapat menjadikan Objek sebagai kunci dan data terkait objek sebagai nilai.

const foo= {name: foo};

const bar= {name: bar};

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

Tetapi kelemahan dari pendekatan ini adalah kompleksitas mengakses nilai dengan kunci, karena kita harus mengulang seluruh array untuk mendapatkan nilai yang diinginkan.

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

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

Kami dapat mengatasi masalah ini karena tidak mendapatkan akses langsung ke nilai dengan menggunakan Peta yang tepat.

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'

Kita bisa melakukan ini menggunakan WeakMap, hanya perlu menulis, const myMap = new WeakMap (). Perbedaan antara Map dan WeakMap adalah WeakMap memungkinkan pengumpulan sampah kunci (di sini objek) sehingga mencegah kebocoran memori, WeakMap hanya menerima objek sebagai kunci, dan WeakMap telah mengurangi serangkaian metode.

2. Peta tidak memiliki batasan nama kunci:

Untuk objek JS biasa kita secara tidak sengaja dapat menimpa properti yang diwarisi dari prototipe dan itu bisa berbahaya. Di sini kita akan menimpa properti toString () dari objek aktor:

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

Sekarang mari kita mendefinisikan fn isPlainObject () untuk menentukan apakah argumen yang disediakan adalah objek biasa dan fn ini menggunakan metode toString () untuk memeriksanya:

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

Peta tidak memiliki batasan pada nama kunci, kita dapat menggunakan nama kunci seperti toString, konstruktor dll. Di sini meskipun objek actorMap memiliki properti bernama toString tetapi metode toString () yang diwarisi dari prototipe objek aktorMap bekerja dengan sempurna.

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

Jika kita memiliki situasi di mana input pengguna membuat kunci maka kita harus mengambil kunci itu di dalam Peta, bukan objek biasa. Ini karena pengguna dapat memilih nama bidang khusus seperti, toString, konstruktor dll. Maka nama-nama kunci tersebut dalam objek biasa berpotensi memecahkan kode yang kemudian menggunakan objek ini. Jadi solusi yang tepat adalah untuk mengikat status antarmuka pengguna ke peta, tidak ada cara untuk memecah Peta:

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

3. Peta dapat diubah:

Untuk mengulangi properti objek biasa, kita perlu Object.entries () atau Object.keys (). Object.entries (plainObject) mengembalikan array pasangan nilai kunci yang diekstraksi dari objek, kita kemudian dapat merusak kunci dan nilai tersebut dan bisa mendapatkan kunci normal dan nilai output.

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

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

Karena Maps dapat diulang, maka kita tidak perlu entri () metode untuk beralih ke Peta dan perusakan kunci, array nilai dapat dilakukan secara langsung pada Peta karena di dalam Peta, setiap elemen berfungsi sebagai array pasangan nilai kunci yang dipisahkan oleh koma .

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'

Map.keys () mengembalikan iterator over keys dan map.values ​​() mengembalikan iterator over values.

4. Kita dapat dengan mudah mengetahui ukuran Peta

Kami tidak dapat secara langsung menentukan jumlah properti dalam objek polos. Kita membutuhkan penolong fn seperti, Object.keys () yang mengembalikan array dengan kunci objek kemudian menggunakan properti panjang kita bisa mendapatkan jumlah kunci atau ukuran objek polos.

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

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

console.log(sizeOfObj);       // 2

Tetapi dalam kasus Maps kita dapat memiliki akses langsung ke ukuran Peta menggunakan properti map.size.

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

console.log(examsMap.size);
djb7861
sumber
1

Dua kiat ini dapat membantu Anda memutuskan apakah akan menggunakan Peta atau Objek:

  • Gunakan peta di atas objek ketika kunci tidak diketahui sampai waktu berjalan, dan ketika semua kunci adalah tipe yang sama dan semua nilai adalah tipe yang sama.

  • Gunakan peta jika ada kebutuhan untuk menyimpan nilai primitif sebagai kunci karena objek memperlakukan setiap kunci sebagai string baik itu nilai angka, nilai boolean atau nilai primitif lainnya.

  • Gunakan objek ketika ada logika yang beroperasi pada elemen individual.

Sumber: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_Collections#Object_and_Map_compared


sumber
2
Kiat-kiat ini tampaknya tidak terlalu membantu terutama karena cenderung tidak mudah untuk memecah hal-hal berdasarkan kriteria tersebut. Saya tidak mengerti mengapa peta bermanfaat jika kunci / nilainya sama. Kedengarannya lebih seperti mencoba mengatakan menggunakan objek seperti kelas / struct, peta seperti koleksi. Yang kedua ditulis dengan buruk tidak sampai pada intinya. Ini benar-benar berarti menggunakan peta ketika Anda memiliki tipe string campuran ("1" dan 1) atau ketika Anda perlu / ingin mempertahankan tipe kunci. Terakhir saya pikir itu sama dengan yang pertama, dengan asumsi Anda tidak tahu apa objek itu sehingga tidak jelas.
jgmjgm
1

Ini adalah cara singkat bagi saya untuk mengingatnya: KOI

  1. Kunci. Kunci objek adalah string atau simbol. Kunci peta juga dapat berupa angka (1 dan "1" berbeda), objek NaN, dll. Digunakan ===untuk membedakan antara kunci, dengan satu pengecualian NaN !== NaNtetapi Anda dapat menggunakannya NaNsebagai kunci.
  2. Memesan. Urutan penyisipan diingat. Jadi [...map]atau [...map.keys()]punya pesanan tertentu.
  3. Antarmuka Obyek: obj[key]atau obj.a(dalam beberapa bahasa, []dan []=benar-benar bagian dari antarmuka). Peta memiliki get(), set(), has(), delete()dll Catatan bahwa Anda dapat menggunakan map[123]tetapi yang menggunakannya sebagai objek JS polos.
nonopolaritas
sumber
0

Menurut Mozilla

Objek vs Peta dalam JavaScript dengan contoh singkat.

Objek- mengikuti konsep yang sama dengan peta yaitu menggunakan pasangan kunci-nilai untuk menyimpan data. Tetapi ada sedikit perbedaan yang membuat peta menjadi pemain yang lebih baik dalam situasi tertentu.

Map- adalah struktur data yang membantu dalam menyimpan data dalam bentuk pasangan. Pasangan terdiri dari kunci unik dan nilai yang dipetakan ke kunci. Ini membantu mencegah bermuka dua.

Perbedaan utama

  • Peta adalah turunan dari suatu objek tetapi sebaliknya tidak benar.

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

  • Di Objek, tipe data bidang kunci dibatasi untuk bilangan bulat, string, dan simbol. Sedangkan di Peta, bidang kunci dapat berupa tipe data apa pun (integer, array, objek)

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)

  • Dalam Peta, urutan elemen asli dipertahankan. Ini tidak benar dalam hal benda.

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

Vahid Akhtar
sumber