這是介紹Array,Set,Object,Map系列的第二篇譯文。
原文連接:戳這裏
按照慣例,詳細的API補充在在文章底部。 javascript
你可能想問,爲何要單獨將Object和Map進行對比,而不是對比Map,Array,或是Object和Set?不一樣於其它兩組,Map和Object有很是多類似的地方須要咱們去更深刻的瞭解和對比,才能分析出他們分別更適合的應用場景。java
Map是一種數據結構(它很特別,是一種抽象的數據結構類型),數據一對對進行存儲,其中包含鍵以及映射到該鍵的值。而且因爲鍵的惟一性,所以不存在重複的鍵值對。
Map即是爲了快速搜索和查找數據而生的。
例如:{(1, "smile"), (2, "cry"), (42, "happy")}
es6
在Map中,每一對數據的格式都爲鍵值對的形式。數組
注:Map中的鍵和值能夠是任何數據類型,不只限於字符串或整數。瀏覽器
JavaScript中的常規對象是一種字典類型的數據結構——這意味着它依然遵循與Map類型相同鍵值對的存儲結構。Object中的key,或者咱們能夠稱之爲屬性,一樣是獨一無二的而且對應着一個單獨的value。bash
另外,JavaScript中的Object擁有內置原型(prototype)。須要注意的是,JavaScript中幾乎全部對象都是Object實例,包括Map。
例如:{1: 'smile', 2: 'cry', 42: 'happy'}
數據結構
從定義上來看,Object和Map的本質都是以鍵值對的方式存儲數據,但實質上他們之間存在很大的區別——app
var map = new Map([[1,2],[3,4]]);
console.log(map instanceof Object); //true
var obj = new Object();
console.log(obj instanceof Map); //false
複製代碼
與數組類似,定義一個Object的方式很是簡單直接:函數
var obj = {}; //空對象
var obj = {id: 1, name: "Test object"};
//2 keys here: id maps to 1, and name maps to "Test object"
複製代碼
或使用構造方法:性能
var obj = new Object(); //空對象
var obj = new Object; //空對象
複製代碼
或者使用Object.prototype.create
var obj = Object.create(null); //空對象
複製代碼
注:
你只能在某些特定的狀況下使用Object.prototype.create
,好比:
var Vehicle = {
type: "General",
display: function(){console.log(this.type);}
}
var Car = Object.create(Vehicle); //建立一個繼承自Vehicle的對象Car
Car.type = "Car"; //重寫type屬性
Car.display(); //Car
Vehicle.display(); //General
複製代碼
在一般狀況下,與數組類似,儘可能避免使用構造函數的方式,理由以下:
var obj = new Object(id: 1, name: "test") //顯然的語法錯誤
var obj1 = {id: 1, name: "test"};
var obj2 = new Object(obj1); //obj1與obj2指向同一個對象
obj2.id = 2;
console.log(obj1.id); //2
複製代碼
建立Map只有一種方式,就是使用其內置的構造函數以及new
語法。
var map = new Map(); //Empty Map
var map = new Map([[1,2],[2,3]]); // map = {1=>2, 2=>3}
複製代碼
語法:
Map([iterable])
Map的構造函數接收一個數組或是一個可遍歷的對象做爲參數,這個參數內的數據都爲鍵值對結構。若是是數組,則包含兩個元素[key, value]
。
Map.prototype.get(key)
實現,這意味着咱們必須先知道該值所對應的keymap.get(1);
複製代碼
Object.<key> and Object[‘key’]
obj.id //1
obj['id'] //1
複製代碼
map.has(1);//return boolean value: true/false
複製代碼
var isExist = obj.id === undefined;
// or
var isExist = 'id' in obj; // 該方法會檢查繼承的屬性
複製代碼
Map與Object語法很類似,不過Map的語法更簡單。
注:咱們可使用Object.prototype.hasOwnProperty()
判斷Object中是否存在特定的key,它的返回值爲true/false
,而且只會檢查對象上的非繼承屬性。
Map.prototype.set()
方法插入元素,該方法接收兩個參數:key,value。若是傳入已存在的key,則將會重寫該key所對應的value。map.set(4,5);
複製代碼
obj['gender'] = 'female'; //{id: 1, name: "test", gender: "female"}
obj.gender = male; // 重寫已存在的屬性
//{id: 1, name: "test", gender: "male"}
複製代碼
正如你所看到的,歸功於其數據結構,兩種插入元素方法的時間複雜度都爲O(1),檢索key並不須要遍歷全部數據。
Object並無提供刪除元素的內置方法,咱們可使用delete
語法:
delete obj.id;
複製代碼
值得注意的是,不少人提出使用一下方法是否會更好,更節約性能。
obj.id = undefined
複製代碼
這兩種方式在邏輯上有很大差異:
delete
會徹底刪除Object上某個特有的屬性obj[key] = undefined
只會改變這個key所對應的value爲undefined
,而該屬性仍然保留在對象中。所以在使用for...in...
循環時仍然會遍歷到該屬性的key。
固然,檢查Object中是否已存在某屬性將在這兩種狀況下產生兩種不一樣的結果,但如下檢查除外:
obj.id === undefined; //結果相同
複製代碼
所以,性能提高在某些狀況下並不適合。
還有一點,delete
操做符的返回值爲true/false
,但其返回值的依據與預想狀況有所差別:
對於全部狀況都返回true
,除非屬性是一個non-configurable
屬性,不然在非嚴格模式返回false
,嚴格模式下將拋出異常。
Map有更多內置的刪除元素方式,好比:
delete(key)
用於從Map中刪除特定key所對應的value,該方法返回一個布爾值。若是目標對象中存在指定的key併成功刪除,則返回true
;若是對象中不存在該key則返回false
。var isDeleteSucceeded = map.delete(1);
console.log(isDeleteSucceeded); //true-
複製代碼
clear()
——清空Map中全部元素。map.clear();
複製代碼
Object要實現Map的clear()
方法,須要遍歷這個對象的屬性,逐個刪除。
Object和Map刪除元素的方法也很是類似。其中刪除某個元素的時間複雜度爲O(1),清空元素的時間複雜度爲O(n),n爲Object和Map的大小。
與Object相比,Map的一個優勢是它能夠自動更新其大小,咱們能夠經過如下方式輕鬆得到:
console.log(map.size);
複製代碼
而使用Object,咱們須要經過Object.keys()
方法計算其大小,該方法返回一個包含全部key的數組。
console.log(Object.keys(obj).length);
複製代碼
Map有內置的迭代器,Object沒有內置的迭代器。
補充:如何判斷某種類型是否可迭代,能夠經過如下方式實現
//typeof <obj>[Symbol.iterator] === 「function」
console.log(typeof obj[Symbol.iterator]); //undefined
console.log(typeof map[Symbol.iterator]); //function
複製代碼
在Map中,全部元素能夠經過for...of
方法遍歷:
//For map: { 2 => 3, 4 => 5 }
for (const item of map){
console.log(item);
//Array[2,3]
//Array[4,5]
}
//Or
for (const [key,value] of map){
console.log(`key: ${key}, value: ${value}`);
//key: 2, value: 3
//key: 4, value: 5
}
複製代碼
或者使用其內置的forEach()
方法:
map.forEach((value, key) => console.log(`key: ${key}, value: ${value}`));
//key: 2, value: 3
//key: 4, value: 5
複製代碼
但對於Object,咱們使用for...in
方法
//{id: 1, name: "test"}
for (var key in obj){
console.log(`key: ${key}, value: ${obj[key]}`);
//key: id, value: 1
//key: name, value: test
}
複製代碼
或者使用Object.keys(obj)
只能獲取全部key並進行遍歷
Object.keys(obj).forEach((key)=> console.log(`key: ${key}, value: ${obj[key]}`));
//key: id, value: 1
//key: name, value: test
複製代碼
好的,問題來了,由於它們在結構和性能方面都很是類似,Map相比Object具備更多的優點,那咱們是否應該更常使用Map代替Object?
儘管,Map相對於Object有不少優勢,依然存在某些使用Object會更好的場景,畢竟Object是JavaScript中最基礎的概念。
get()
方法)。var obj = {
id: 1,
name: "It's Me!",
print: function(){
return `Object Id: ${this.id}, with Name: ${this.name}`;
}
}
console.log(obj.print());//Object Id: 1, with Name: It's Me.
複製代碼
(你可使用Map進行嘗試,然而Map並不能實現這樣的數據結構)
delete
對Object的屬性進行刪除操做存在不少性能問題。因此,針對於存在大量增刪操做的場景,使用Map更合適。如何選擇Object和Map取決於你要使用的數據類型以及操做。
當咱們只須要一個簡單的可查找存儲結構時,Map相比Object更具優點,它提供了全部基本操做。但在任何意義上,Map都不能替代Object。由於在Javascript中,Object畢竟不只僅是一個普通的哈希表(所以Object不該該用做普通哈希表,它會浪費不少資源)。
其餘資料連接:
Object - MDN - Mozilla
Map - MDN - Mozilla
ES6入門 - 阮一峯