ES6之Map使用

假设你是开发人员,在网站上每个文本——例如“Ninjas for hire”——你需要创建对应的语言,例如日语,汉语,韩语等。这种集合,将key映射到指定的值上,在不同的编程语言中具有不同的名称,通常称为字典或Map。

在JavaScript中如何有效地管理这种定位呢?一种传统的定位方法是利用对象是属性名和属性值的特性。

 

创建如下字典:

 

上面的方法看似是一种完美的解决方案,但是这种方法并不可靠。

别把对象当做Map

<script type="text/javascript">

const dictionary = {

'ja': {

"Ninjas for hire": '1234456'

},

'zh': {

"Ninjas for hire": 'aaaaaaa'

},

"ko": {

"Ninjas for hire": 'bbbbbbb'

}

}

alert(dictionary.ja['Ninjas for hire'] === '1234456');



if (dictionary.ja['Ninjas for hire'] === '1234456') {

alert(dictionary.ja['Ninjas for hire']);

}



alert("before:" + dictionary.ja['constructor']);



if (typeof dictionary.ja['construtor'] === 'undefined') {

alert(dictionary.ja['constructor']);//别打错了constructor

}

</script>

 

运行结果:

因为试图访问constructor属性,这是在字典中未定义的单词。在本例中我们期望返回undefined。但是结果并非如此。

实际返回的是function Object() { [native code] }

这是为什么呢?每个对象都有原型,尽管定义新的空对象作为map,仍然可以访问原型对象的属性。原型对象的属性之一是constructor(回顾一下,constructor是原型对象的原型,指回构造函数本身),它正是造成混乱的罪魁祸首。

同时,对象的key必须是字符串。如果想映射为其它类型,它会默默转化为字符串,没有任何提示。

/**********************************************************************************************/

<script>

const firstElement = document.getElementById("firstElement");

const secondElement = document.getElementById("secondElement");



//定义空对象,使用映射存储html节点的额外信息

const map = {}



//存储第一个元素信息,并且验证是否正确存储

map[firstElement] = {data: 'firstElement'};



//alert(map[firstElement].data === 'firstElement');

if (map[firstElement].data === 'firstElement') {

alert("The firstElement is correctly mapped");

}



//存储第二个元素信息,并验证是否正确存储

map[secondElement] = {data: 'secondElement'};



if (map[secondElement].data === 'secondElement') {

alert("The second element is correctly overriden");

}



//第1个元素的映射关系无效

if (map[firstElement].data === 'firstElement') {

alert("But now the firstElement is overriden");

}

</script>

 

在上面的代码清单中,我们创建了两个HTML元素:firstElement个secondElement,通过docunment.getElementById方法从DOM中获取到这两个元素。为了存储每个元素的更多信息,就定义一个JavaScript对象。

<!-- 创建两个HTML元素,并分别通过document.getElementById获取-->

<div id="firstElement"></div>

<div id="secondElement"></div>



<script>

const firstElement = document.getElementById("firstElement");

const secondElement = document.getElementById("secondElement");



//定义空对象,使用映射存储html节点的额外信息

const map = {}



//存储第一个元素信息,并且验证是否正确存储

map[firstElement] = {data: 'firstElement'};



//alert(map[firstElement].data === 'firstElement');

if (map[firstElement].data === 'firstElement') {

alert("The firstElement is correctly mapped");

}



//存储第二个元素信息,并验证是否正确存储

map[secondElement] = {data: 'secondElement'};



if (map[secondElement].data === 'secondElement') {

alert("The second element is correctly overriden");

}



//第1个元素的映射关系无效

if (map[firstElement].data === 'firstElement') {

alert("But now the firstElement is overriden");

}

</script>

 

我们创建了两个HTML元素:firstElement和secondElement,通过document.getElementById方法从DOM中获取到两个元素。为了存储每个元素的更多信息,我们定义了一个Javascript对象:

const map = {}

然后使用HTML元素作为对象的key值,存储一些数据:

map[firstElement] = {data: 'firstElement'};

然后检查数据。对第二个元素执行相同的过程:

map[secondElement] = {data: 'secondElement'};

看起来一切很完美,成功将数据关联到HTML元素上。但是,如果访问第一个元素就会出现问题:

map[firstElement].data

预期是可以取回第一个元素的对应数据,但是事实并非如此,但会的其实是第二个元素对象。

上面代码的执行结果如下:

1.第一次然后检查数据

 

2.第二次检查元素

 

3.然而第三次获取元素就失败了

//第1个元素的映射关系无效,注意,只要改为if (map[firstElement].data === 'secondElement')就可以了。

if (map[firstElement].data === 'firstElement') {

alert("But now the firstElement is overriden");

}

这是因为对象的key必须是字符串,这意味着当视图使用非字符串类型如HTML元素作为key时,其值被toString方法静默转换为字符串类型。HTML元素转换为字符串后的值为[object HTMLDivElement],第1个元素的数据信息被存储在[object HTMLDivElement]属性中。接着,当试图为第二个元素创建映射时,发生了相同的过程。第2个元素也是HTML元素,也被转换成为字符串,对应的数据也被存储在[object HTMLDivElement]属性上,覆盖了第一个元素的值。由于这两个原因:原型继承属性以及key仅支持字符串,所以通常不能使用对象作为map。由于这种限制,ECMAScript委员会定义了一个全新的类型Map。

/************************************************************************************/

<script>

const ninjaIslandMap = new Map();//使用Map构造函数创建map



//定义3个ninja对象

const ninja1 = { name: "Yoshi"};

const ninja2 = { name: 'Hattori'};

const ninja3 = { name: "Kuma"};



//使用Map的set方法,建立两个ninja对象的映射关系

ninjaIslandMap.set(ninja1, {homeIsland: "Hongshu"});

ninjaIslandMap.set(ninja2, {homeIsland: "Hokkaido"});



//使用Map的get方法。获取ninja对象

if (ninjaIslandMap.get(ninja1).homeIsland === "Hongshu") {

console.log("The first mapping works");

}

if (ninjaIslandMap.get(ninja2).homeIsland === "Hokkaido") {

console.log("The second mapping works");

}





//验证第三个ninja不存在映射关系

if (ninjaIslandMap.get(ninja3) === undefined) {

console.log("There is no mapping for the third mappings");

}



//验证map中只存在前两个对象的映射,不存在第三个对象的映射

if (ninjaIslandMap.size === 2) {

console.log("we are creating two mappings");

}



//使用has验证map中是否存在指定的key

if (ninjaIslandMap.has(ninja1) && ninjaIslandMap.has(ninja2)) {

console.log("We have mappins for the first two nijas");

}



if (!ninjaIslandMap.has(ninja3)) {

console.log("But not for the third nija!");

}



//使用delete方法从map删除key

ninjaIslandMap.delete(ninja1);



if (!ninjaIslandMap.has(ninja1) && ninjaIslandMap.size === 1) {

console.log("There is no first ninja mapping any more!");

}



//使用clear方法完全清空map

ninjaIslandMap.clear();



if(ninjaIslandMap.size === 0) {

console.log("All mappings have been cleared!");

}

</script>


 

运行结果如下:

 

在本例中,调用Map的构造函数创建map:

const ninjaIslandMap = new Map();

然后创建三个ninja对象,分别命名为ninja1,ninja2,nija3.使用set方法:

ninjaIslandMap.set(ninja1, {homeIsland: "Hongshu"});

通过get方法获取前两个ninja对象的映射:

ninjaIslandMap.get(ninja1).homeIsland === "Hongshu"

只有前两个ninja对象存在映射,第三个对象不存在映射,因为第三个对象没有被set调用。当前map的状态如下图:

 

 

除了get和set方法以外,map还具有size属性以及has/delete方法。size属性告诉我们已经创建了多少个映射。

has方法用于判断指定的key是否存在:

ninjaIslandMap.has(ninja1) && ninjaIslandMap.has(ninja2)

delete方法用于删除映射:

ninjaIslandMap.delete(ninja1);

clear方法用于清空map:

ninjaIslandMap.clear();

 

key相等

const map = new Map();

const currentLocation = location.href;//使用内置的location.href属性获取当前页的URL

const firstLink = new URL(currentLocation);//创建两个当前页面的链接

const secondLink = new URL(currentLocation);



map.set(firstLink, {description: 'firstLink'});//分别为两个链接添加映射

map.set(secondLink, {description: 'secondLink'});



//尽管每个链接指向相同的值,但是仍然具有各自的映射

if (map.get(firstLink).description === 'firstLink') {

console.log("First link mapping");

}

if (map.get(secondLink).description === 'secondLink') {

console.log("Second link mapping");

}

if (map.size === 2) {

console.log("There are two mapping");

}

 

上面使用location.herf属性获取当前页面的URL。然后使用URL构造函数创建两个URL当前页面链接的对象。接着对每个链接对象关联描述信息。最后,检查映射是否正确创建。

常用JavaScript的同学不会觉得出乎意料:两个不同的对象创建不同的映射。但是,两个URL对象指向相同的URL地址:当前页面的地址。我们也许会怀疑两个对象应该相等。但是,在JavaScript中,我们不能重载相等运算符,虽然两个对象的内容相同,但是两个对象仍然不相等。

 

遍历map

 

使用map的一些优点:

可以确定map中只存在你放入的内容,可以使用任意类型的数据作为key等。但还有更多优点!

因为map是集合,可以使用forof循环遍历map。也可以确保遍历的顺序与插入的顺序一致。

//创建一个新的map

const directory = new Map();

directory.set("Yoshi", "12345678");

directory.set("Kuma", "126930946294");

directory.set("Hiro", "128489506000");



console.log('----------------------------------------');



//使用for...of循环遍历directory。每个元素具有两个值:key与value

for (let item of directory) {

if (item[0] !== null) {

console.log("Key:" + item[0]);

}



if (item[1] !== null) {

console.log("Value:" + item[1]);

}

}



console.log('----------------------------------------');



//可以使用内置的keys方法遍历所有的key

for (let key of directory.keys()) {

if (key !== null) {

console.log("Key:" + key);

if (directory.get(key) != null) {

console.log("Value:" + directory.get(key));

}

}

}



console.log('----------------------------------------');



//可以使用values方法遍历所有value

for (var value of directory.values()) {

if (value !== null) {

console.log("Value:" + value);

}

}



console.log('----------------------------------------');

运行结果: 

 

在每个迭代中,每个元素是具有两个值的数组,第1个值是key,第二个值是value,也可以分别使用keys与values方法遍历key和value。

 

参考书籍《JavaScript忍者秘籍》

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值