[译]ECMAScript 6中的集合类型,第二部分:Map
2012-10-10 14:13
176 查看
原文:http://www.nczonline.net/blog/2012/10/09/ecmascript-6-collections-part-2-maps/
Map[1]和Set一样,也是在其他语言中经常会用到的东西.其基本思想就是把一个值映射给一个唯一的键,这样在任何时刻,都能根据这个键获取到对应的值.在过去,JavaScript开发者们一直都把常规的对象当成Map来使用(译者注:之所以说常规对象,是因为Map类型也是对象).实际上,JSON就是基于"对象是由键-值对组成的"这个前提发明的.可是,对象当作Set来使用的限制同样存在于对象当作Map使用的情况下,那就是:键只能是字符串.
译者注:也许你这里会疑惑:不对啊,数组的索引不是数字类型吗?但其实:
在ECMAScript 6未完全实现之前,你可能看到过下面这样的代码:
上面的代码使用了一个常规的对象作为Map来使用,检查一个给定的键是否存在.这里最大的限制就是键始终会被转换成字符串.如果你确定自己的键不可能是非字符串,倒也没什么太大的影响.如果你想存储一些与某个特定的DOM元素相关的数据的话,你也许会这么做:
不幸的是,元素对象会被转换成
ECMAScript 6中的
在这个例子中,一共存储了两个键值对.键
Map有几个和Set相同名称的方法,比如
为了能够更方便的将大量数据添加到map中,你可以向
在你想操作map中存储的数据时,你有三种生成器方法可以使用,
在单纯遍历键或者值时,你会在每个循环内部得到一个单个的值.但在遍历一个键-值对时,你会得到一个数组,该数组的第一个元素是键,第二个元素是对应的值.
另外一种可以遍历map的键-值对的方式是使用
另外一个和数组的
这里,回调函数中的this值指向了
和以前遍历常规对象的方式比较一下:
在使用常规对象充当map的角色时,始终要考虑的就是,`for-in`循环可能会遍历到原型上的属性.你必须使用`hasOwnProperty()`方法来确保这个属性的确是对象自己的.当然,如果这个对象可能有自定义的方法,你也得过滤掉:
Map的遍历功能可以让你更专注与要遍历的数据本身,而不用担心其他数据的干扰.这也是用Map存储键-值对比用常规数组更好的一点.
译者注:Firefox17(以及更新的18,19)已经实现了用for-of进行Set和Map的遍历操作,但还没实现forEach()方法
译者注:为了兼容那些旧的浏览器,可以使用这个实现了ES6中多种集合类型(Set,Map,WeakMap,HashMap)的shim:http://benvie.github.com/harmony-collections/
由于目前ECMAScript 6规范草案还没有完成.因此,Map还被认为是实验性的API,在最后规范定稿之前,可能还会发生一些变化.目前所有关于ECMAScript 6的文章都应该被视为是对未来的展望.虽然这些实验性的API已经在一些浏览器中实现了,但最好不要使用在实际的生产环境中.
ECMAScript 6 Draft Specification (ECMA)
Map[1]和Set一样,也是在其他语言中经常会用到的东西.其基本思想就是把一个值映射给一个唯一的键,这样在任何时刻,都能根据这个键获取到对应的值.在过去,JavaScript开发者们一直都把常规的对象当成Map来使用(译者注:之所以说常规对象,是因为Map类型也是对象).实际上,JSON就是基于"对象是由键-值对组成的"这个前提发明的.可是,对象当作Set来使用的限制同样存在于对象当作Map使用的情况下,那就是:键只能是字符串.
译者注:也许你这里会疑惑:不对啊,数组的索引不是数字类型吗?但其实:
> let arr = ["a","b","c"] //定义一个数组 > arr[0] //之所以能用数字作为索引,是因为0会被自动转换为"0" "a" > arr["0"] //这才是真实的索引 "a" >arr[{toString:function(){return 0}}] //对象当作索引也会被转换为字符串 "a" >Object.keys(arr) //获取数组的索引,可以看到其实是字符串 ["0", "1", "2"] > typeof Object.keys(arr)[0] //用typeof再确认一下 "string"
在ECMAScript 6未完全实现之前,你可能看到过下面这样的代码:
//定义一个对象,当作map来使用 var map = {}; //如果不存在这个键,则为这个键赋值 if (!map[key]) { map[key] = value; }
上面的代码使用了一个常规的对象作为Map来使用,检查一个给定的键是否存在.这里最大的限制就是键始终会被转换成字符串.如果你确定自己的键不可能是非字符串,倒也没什么太大的影响.如果你想存储一些与某个特定的DOM元素相关的数据的话,你也许会这么做:
// 元素对象会被转换成字符串 var data = {}, element = document.getElementById("my-div"); data[element] = metadata;
不幸的是,元素对象会被转换成
"[Object HTMLDivElement]"或者其他类似的字符串(不同浏览器上的值可能会不同).问题就有了:所有的
<div>元素都会被转换为相同的一个字符串,也就意味着虽然你使用了不同的对象作为键,但读取或写入的都是同一个键对应的值.因此,
Map类型对JavaScript来说将会非常有用.
ECMAScript 6中的
Map类型是个有序的键-值对列表,最关键的是键和值都可以是任意类型.
5和
"5"是两个完全不同的键,键的使用规则和set中值的规则是一样的:
NaN被认为和另一个
NaN是相同的,
-0和
+0是不同的值,也就是说内部不是用
===来进行判断的.你可以使用
set()和
get()方法从一个map中存储和获取数据,如下:
//定义Map var map = new Map(); //存储数据 map.set("name", "Nicholas"); map.set(document.getElementById("my-div"), { flagged: false }); //获取数据 var name = map.get("name"), meta = map.get(document.getElementById("my-div"));
在这个例子中,一共存储了两个键值对.键
"name"存储了一个字符串,键
document.getElementById("my-div")存储了一个DOM元素的元数据.如果某个键不存在于一个map中,则在调用
get()方法时会返回undefined.
Map有几个和Set相同名称的方法,比如
has()方法用来判断某个键是否存在与一个map中,
delete()方法用来删除一个map中的某个键-值对.还可以使用
size()方法获取到一个map中的键-值对个数:
var map = new Map(); map.set("name", "Nicholas"); console.log(map.has("name")); // true console.log(map.get("name")); // "Nicholas" console.log(map.size()); // 1 map.delete("name"); console.log(map.has("name")); // false console.log(map.get("name")); // undefined console.log(map.size()); // 0
为了能够更方便的将大量数据添加到map中,你可以向
Map构造函数中传入一个包含数组的数组.由于在引擎内部,每个键-值对都是以一个包含两个元素的数组存储的,第一个元素作为键,第二个作为值.整个map就是一个包含了这些数组的数组,map可以用这样的形式初始化也就不奇怪了:
var map = new Map([ ["name", "Nicholas"], ["title", "Author"]]); console.log(map.has("name")); // true console.log(map.get("name")); // "Nicholas" console.log(map.has("title")); // true console.log(map.get("title")); // "Author" console.log(map.size()); // 2
在你想操作map中存储的数据时,你有三种生成器方法可以使用,
keys():遍历map中的所有键;
values():遍历map中的所有值;
items():遍历map中的所有键-值对(
items()是遍历map时默认的生成器方法).通常使用
for-of循环来配合上面的几个方法:
for (let key of map.keys()) { console.log("Key: %s", key); } for (let value of map.values()) { console.log("Value: %s", value); } for (let item of map.items()) { console.log("Key: %s, Value: %s", item[0], item[1]); } // 相当于map.items() for (let item of map) { console.log("Key: %s, Value: %s", item[0], item[1]);
在单纯遍历键或者值时,你会在每个循环内部得到一个单个的值.但在遍历一个键-值对时,你会得到一个数组,该数组的第一个元素是键,第二个元素是对应的值.
另外一种可以遍历map的键-值对的方式是使用
forEach()方法.该方法和数组的f
orEach()方法类似.你传入一个回调函数,在回调函数执行时,会自动传入三个参数:值,键,以及map本身.例如:
map.forEach(function(value, key, map)) { console.log("Key: %s, Value: %s", key, value); });
另外一个和数组的
forEach()方法相同的一点是,你可以传入第二个可选的参数来指定回调函数中使用的this值:
var reporter = { report: function(key, value) { console.log("Key: %s, Value: %s", key, value); } }; map.forEach(function(value, key, map) { this.report(key, value); }, reporter);
这里,回调函数中的this值指向了
reporter.也就可以使用
this.report()了.
和以前遍历常规对象的方式比较一下:
for (let key in object) { // 确保这个属性不是继承自原型! if (object.hasOwnProperty(key)) { console.log("Key: %s, Value: %s", key, object[key]); } }
在使用常规对象充当map的角色时,始终要考虑的就是,`for-in`循环可能会遍历到原型上的属性.你必须使用`hasOwnProperty()`方法来确保这个属性的确是对象自己的.当然,如果这个对象可能有自定义的方法,你也得过滤掉:
for (let key in object) { // 确保该属性不是继承自原型且属性值不是一个函数! if (object.hasOwnProperty(key) && typeof object[key] !== "function") { console.log("Key: %s, Value: %s", key, object[key]); } }
Map的遍历功能可以让你更专注与要遍历的数据本身,而不用担心其他数据的干扰.这也是用Map存储键-值对比用常规数组更好的一点.
浏览器支持
目前Firefox和Chrome都已经实现了Map类型,只是在Chrome中,你必须先通过下面的操作激活ECMAScript 6的新特性:打开页面
chrome://flags,勾选“启用实验性 JavaScript”.需要注意的是,这两个浏览器的Map实现都不够完全.都没有实现Set的
for-of迭代,Chrome的实现还缺少
size()方法(属于ECMAScript 6规范草案的一部分[2]
)和Map构造函数不能用一个数组的数组来进行初始化.
译者注:Firefox17(以及更新的18,19)已经实现了用for-of进行Set和Map的遍历操作,但还没实现forEach()方法
译者注:为了兼容那些旧的浏览器,可以使用这个实现了ES6中多种集合类型(Set,Map,WeakMap,HashMap)的shim:http://benvie.github.com/harmony-collections/
总结
ECMAScript 6中的Map类型给我们带来一个很重要的,将来定会被频繁使用的特性.开发者们长期以来一直想要一个更可靠的方式来存储键-值对,以替代目前依赖于常规对象的情形.Map给我们提供了所有常规对象无法实现的功能, 包括更简单的遍历键和值以及无需担心对象原型的干扰.由于目前ECMAScript 6规范草案还没有完成.因此,Map还被认为是实验性的API,在最后规范定稿之前,可能还会发生一些变化.目前所有关于ECMAScript 6的文章都应该被视为是对未来的展望.虽然这些实验性的API已经在一些浏览器中实现了,但最好不要使用在实际的生产环境中.
参考
Simple Maps and Sets (ES6 Wiki)ECMAScript 6 Draft Specification (ECMA)
相关文章推荐
- [译]ECMAScript 6中的集合类型,第三部分:WeakMap
- 将你的文件类型集合进XML编辑器(第二部分)
- C++ 模板类型萃取技术 第二部分 基于泛型的类型萃取技术
- 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第二部分
- 第二部分 类型与通用语言运行时----------------读《Microsoft.NET框架程序设计》
- fastjson json字符串和JavaBean、List、Map及复杂集合类型的相互转换(二)
- 《C++捷径教程》读书笔记--Chapter 9--更多的数据类型与运算符(第二部分)
- 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第二部分
- Jquery中遍历map类型集合
- Hibernate 多表关联映射- Hibernate中使用的集合类型(set,list,array,bag,map)
- Hibernate逍遥游记-第12章 映射值类型集合-004映射Map(<map-key>)
- 如何将一个List<Map<String,String>>类型的集合数据转换成json格式输出
- java 集合框架 - Map集合部分
- 第二部分 算法的基本控制结构与数据类型
- Map集合,HashMap,HashTable,ConCurrentHashMap,利用Iterator输出Map集合,自定义Map的key类型,TreeMap子类详解
- Hibernate集合映射之Map-----element映射基本类型值
- Hibernate之映射值类型集合(组件的集合之map)
- 如何制作一款像超级玛丽兄弟一样基于平台的游戏-第二部分 (xcode,物理引擎,TMXTiledMap相关应用)
- 【C语言探索之旅】 第二部分第六课:创建你自己的变量类型