这是介绍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入门 - 阮一峰