es6语法特性进阶(6)

Map数据结构
ECMAScript 6 中的 map 类型包含一组有序的键值对,其中键和值可以是任何类型。
​键的比较结果由 Object.is() 来决定,所以你可以同时使用 5 和 “5” 做为键来存储,因为它们是不同的类型。
​这和使用对象属性做为值的方法大相径庭,因为 对象的属性会被强制转换为字符串类型。

创建Map对象和Map的基本的存取操作
1.Map创建也是使用Map构造函数
2.向Map存储键值对使用set(key, value);方法
3.可以使用get(key),来获取指定key对应的value

1
2
3
4
5
6
7
8
var map = new Map();
map.set("a", "lisi");
map.set("b", "zhangsan");
map.set("b", "zhangsan222"); // 第二次添加,新的value会替换掉旧的
console.log(map.get("a"));
console.log(map.get("b")); //zhangsan222
console.log(map.get("c")); //undefined.如果key不存在,则返回undefined
console.log(map.size); //2

Map与Set类似的3个方法
has(key) - 判断给定的 key 是否在 map 中存在
delete(key) - 移除 map 中的 key 及对应的值
clear() - 移除 map 中所有的键值对

初始化Map
创建Map的时候也可以像Set一样传入数组。但是传入的数组中必须有两个元素,这个两个元素分别是一个数组。
也就是传入的实际是一个二维数组!

1
2
3
4
5
6
7
8
9
//map接受一个二维数组
var map = new Map([
//每一个数组中,第一个是是map的可以,第二个是map的value。如果只有第一个,则值是undefined
["name", "lisi"],
["age", 20],
["sex", "nan"]
]);
console.log(map.size);
console.log(map.get("name"))

Map的forEach方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var map = new Map([
["name", "李四"],
["age", 20],
["sex", "nan"]
]);
/*
回调函数有函数:
参数1:键值对的value
参数2:键值对的key
参数3:map对象本身
*/
map.forEach(function (value, key, ownMap) {
console.log(`key=${key} ,vlue=${value}`);
console.log(this);
})

迭代器(iterator)和for…of循环
循环问题

1
2
3
4
var colors = ["red", "green", "blue"];
for (var i = 0, len = colors.length; i < len; i++) {
console.log(colors[i]);
}

上面的代码写起来简单,但是实际使用的过程中,我们需要自己去控制变量,如果有嵌套的情况下,还要控制多个变量,很容易出错。
迭代器就是为了解决这个问题的。

什么是迭代器
迭代器是一个对象
迭代器提供一个方法next() 这个方式总是能够返回迭代到的对象。
next返回的对象中,至少有两个属性:done 是一个boolean值(表示数据是否迭代完)。 value:具体的数据(迭代到的具体数据)
​ 迭代器只是带有特殊接口(方法)的对象。所有迭代器对象都带有 next() 方法并返回一个包含两个属性的结果对象。这些属性分别是 value 和 done,前者代表下一个位置的值,后者在没有更多值可供迭代的时候为 true 。迭代器带有一个内部指针,来指向集合中某个值的位置。当 next() 方法调用后,指针下一位置的值会被返回。

​ 若你在末尾的值被返回之后继续调用 next(),那么返回的 done 属性值为 true,value 的值则由迭代器设定。该值并不属于数据集,而是专门为数据关联的附加信息,如若该信息并未指定则返回 undefined 。迭代器返回的值和函数返回值有些类似,因为两者都是返回给调用者信息的最终手段。

我们可以用ES5之前的知识手动创建一个迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function createIterator(items) {
var i = 0;
return {
next: function() {
var done = (i >= items.length);
var value = !done ? items[i++] : undefined;
return {
done: done,
value: value
};
}
};
}
//创建一个可以在指定数组上面迭代的迭代器对象。
var iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// for all further calls
console.log(iterator.next()); // "{ value: undefined, done: true }"

从以上的示例来看,根据 ECMAScript 6 规范模拟实现的迭代器还是有些复杂。

幸运的是,ECMAScript 6 还提供了生成器,使得迭代器对象的创建容易了许多。

生成器函数
生成器函数就是返回迭代器的函数!
生成器函数由 function 关键字和之后的星号(*)标识,同时还能使用新的 yield 关键字。
看下面代码:

1
2
3
4
5
6
7
8
9
10
11
12
//生成器函数。 注意中间的 * 不能丢
function * createIterator() {
//每个yield的后面的值表示我们迭代到的值。 yield也定义了我们迭代的顺序。
yield 3;
yield 4;
yield 2;
}
var it = createIterator();
console.log(it.next().value); // 2
console.log(it.next().value); // 4
console.log(it.next().value); // 2
console.log(it.next().value); //undefined

迭代器函数也是函数,所以他可以像正常的函数一样调用,但是迭代器生成器函数会自动返回一个迭代器对象。
每调用一次迭代器的next方法,如果碰到 yield 都会返回一个迭代到的一个对象,然后停止执行,直到下次调用next方法,会从上次停止的地方继续执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//这个迭代器函数返回的迭代器可以迭代传入的数组中的所有元素。
function *createIterator(items) {
for (let i = 0; i < items.length; i++) {
//每调用一次next,碰到yild程序就会停止,并返回迭代到的对象 {value : items[i], done : true}
yield items[i];
}
}
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 进一步调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

注意:
yield 关键字只能 直接用在生成器内部 。在其它地方甚至是生成器内部的函数中使用都会抛出语法错误。

生成器函数表达式
你可以使用函数表达式来创建生成器,只需在 function 关键字和圆括号之间添加星号(*)。例如:

1
2
3
4
5
6
7
8
9
10
11
12
let createIterator = function *(items) {
for (let i = 0; i < items.length; i++) {
yield items[i];
}
};
let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: 3, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
// 进一步调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

注意:无法使用箭头函数来创建生成器。

可迭代类型和for-of迭代循环
迭代器的主要工作就是迭代数据,但是不是所有的数据都是可以迭代的。
​与迭代器紧密相关的是,可迭代类型是指那些包含 Symbol.iterator 属性(方法)的对象。
​该 symbol 类型定义了返回迭代器的函数。在 ECMAScript 6 中,所有的集合对象(数组,set 和 map)与字符串都是可迭代类型,因此它们都有默认的迭代器。可迭代类型是为了 ECMAScript6 新添加的 for-of 循环而设计的。

换句话说,默认情况下只有 数组、set、Map和字符串才可以使用迭代器去迭代。 (也就可以使用for…of了)
for…of循环只迭代出来的元素,根本不管索引!不管索引!不管索引!重要的问题重复三遍!
使用 for…of 迭代数组:

1
2
3
4
var arr = ["a", "c", "b", "d"];
for(var item of arr){
console.log(item)
}

使用 for…of 迭代Set:

1
2
3
4
var set = new Set(["a", "c", "b", "d"]);
for(var item of set){
console.log(item)
}

使用 for…of 迭代Map:

1
2
3
4
5
var map = new Map([["name", "lisi"],["sex", "男"],["age", 20]]);
map.set("aaa", "bbb")
for(var item of map){
console.log(item); //注意:这里迭代到的是由key和value组成的数组。
}

使用for … of迭代字符串

1
2
3
4
var s = "abcd";
for(let c of s){
console.log(c)
}

注意:for…of 只能迭代可以迭代的对象,对于非可迭代对象使用for…of会抛出异常

说明:以数组为例。
​ for-of 循环首先会调用 values 数组的 Symbol.iterator 方法来获取迭代器(Symbol.iterator 方法由幕后的 JavaScript 引擎调用)。之后再调用 iterator.next() 并将结果对象中的 value 属性值,即 1,2,3,依次赋给 num 变量。当检测到结果对象中的 done 为 true,循环会退出,所以 num 不会被赋值为 undefined 。
​ 如果你只想简单的迭代数组或集合中的元素,那么 for-of 循环比 for 要更好。for-of 一般不容易出错,因为要追踪的条件更少。所以还是把 for 循环留给复杂控制条件的需求吧。

访问可迭代类型的默认迭代器
Symbol.iterator是可迭代类型的一个方法,调用这个方法就可以获取到他的默认迭代器。

1
2
3
let s = "abcd";
let it = s[Symbol.iterator](); //调用字符串的Symbol.iterator方法
console.log(it.next()); //返回迭代器迭代到的第一个对象

因为Symbol可以返回一个对象的默认迭代器,所以我们可以使用它来判断一个对象是否可迭代

1
2
3
4
5
6
7
8
function isIterable(object) {
return typeof object[Symbol.iterator] === "function";
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable("Hello")); // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable({"name":"李四"})); // false。普通对象不可迭代

开发者自定义的对象默认是不可迭代类型,但是你可以为它们创建 Symbol.iterator 属性并指定一个生成器来使这个对象可迭代。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let collection = {
items: [],
*[Symbol.iterator]() {
for (let item of this.items) {
yield item;
}
}
};
collection.items.push(1);
collection.items.push(2);
collection.items.push(3);
for (let x of collection) {
console.log(x);
}

访客的ip和所在地址: 访问时间: 当前时间:

殖民 wechat
欢迎您扫一扫上面的微信公众号
打赏不在多少,请鼓励一下!