ES6 迭代协议
栏目: JavaScript · 发布时间: 8年前
内容简介:ES6 迭代协议
ES6 中引入的迭代器和 Python 的迭代器类型非常类似,可互相参考进行学习。
迭代器贯穿 ES6 的始终,它是数据和循环的新标准。
迭代协议
-
有两个迭代协议:可迭代协议和迭代器协议
- 可迭代协议 允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 结构中什么值可以被循环(得到)
- 迭代器协议 定义了一种标准的方式(调用 next() 方法)来产生一个有限或无限序列的值
-
可迭代协议 —> 可迭代对象
-
迭代器协议 —> 迭代器对象
可迭代对象
一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是可迭代的,即它是一种可迭代对象(iterable)。
ES6 规定,默认的 Iterator 接口部署在数据结构的 Symbol.iterator
属性上。
-
一些内建类型拥有默认的迭代器行为,其他类型(如 Object )则没有。
下表中的内建类型拥有默认的
Symbol.iterator方法:
即:Array、TypedArray、String、Map、Set 是 JavaScript 内置的可迭代对象
Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的迭代器生成函数。 执行这个函数,就会返回一个迭代器。
至于属性名 Symbol.iterator
,它是一个表达式,返回 Symbol 对象的 iterator 属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内使用。
-
通过重写
Symbol.iterator方法来重新定义迭代行为
let arr = ['a', 'b', 'c'];
arr[Symbol.iterator] = function(){
return {
_first: true,
next: function(){
if (this._first) {
this._first = false;
return {
value: "only one",
done: false
};
} else {
return {
done: true
};
}
}
};
};
for (let x of arr) {
console.log(x)
}
// 只打印 'only one'
- 一些语句和表达式是预料会用于可迭代对象 ,比如 for-of 循环,spread operator(扩展运算符), yield* 和 destructuring assignment(解构赋值)
for(let x of 'abc'){
console.log(x);
}
// 打印 'a'、'b'、'c'
console.log([...'abc']); // ["a", "b", "c"]
function*gen(){
yield*["a", "b", "c"];
}
let g = gen();
g.next(); // Object {value: "a", done: false}
g.next(); // Object {value: "b", done: false}
g.next(); // Object {value: "c", done: false}
g.next(); // Object {value: undefined, done: true}
let arr = ['a', 'b', 'c']; let [x, y, z] = arr; console.log(x); // a console.log(y); // b console.log(z); // c
for…of 语句
ES6 之前,我用常用 forEach 方法来遍历数组,但是数组的 forEach 函数有个缺陷: 不能正确处理 continue(跳过本次循环)、break(中断循环)、return(返回到外层函数) 语句的逻辑 。
ES6 发布后,推出了新的 for...of
语句,用来遍历数组以及新的 Set、Map 数据结构,并且解决了上面 forEach 的缺陷。
-
for…of 循环可迭代的数据结构: 数组、 大部分类数组对象、字符串、Map、Set
- 不支持直接迭代普通的对象,但可以迭代 Object.keys(obj)
for(let x of ['aa',123,4]){
if(x === 123){
continue;
}
console.log(x);
}
// aa
// 4
-
支持 for…of 循环遍历的对象都直接或间接地部署了 Iterator 接口,即在
Symbol.iterator属性上部署了迭代器生成方法(原型链上的对象具有该方法也可) -
for-of 循环的内部原理:首先调用集合的 Symbol.iterator 方法,紧接着返回一个新的迭代器对象,for-of 循环将重复调用这个迭代器对象的
next()方法,每次循环调用一次,直到next().done为true结束。 -
并不是所有的类数组对象都具有 Iterator 接口
- 一个解决办法是使用 Array.from(arrayLike) 方法将类数组对象转换为数组
- 或者直接将类数组对象的 Iterator 接口部署为数组的 Iterator 接口
let arrayLike = {
0: 'a',
1: 'b',
length: 2
};
// 方法一
for (let x of Array.from(arrayLike)) {
console.log(x);
}
// 方法二
arrayLike[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者 arrayLike[Symbol.iterator] = [][Symbol.iterator];
for (let x of arrayLike) {
console.log(x);
}
迭代器对象
迭代器对象是一种具有 .next() 方法的特殊的可迭代对象。
let arr = ['hello', 'vue', 'js'];
for (let x of arr) {
console.log(x);
}
// 分别打印 'hello'、'vue'、'js'
for (let x of arr[Symbol.iterator]()) {
console.log(x);
}
// 分别打印 'hello'、'vue'、'js'
- 将一个可迭代对象转换为一个迭代器对象,直接调用可迭代对象的 [Symbol.iterator] 方法
let arr = ['hello', 'vue', 1314, [1, 2, 3]];
let arrIterator = arr[Symbol.iterator]();
console.log(arrIterator.next());
// Object {value: "hello", done: false}
console.log(arrIterator.next());
// Object {value: "vue", done: false}
console.log(arrIterator.next());
// Object {value: 1314, done: false}
console.log(arrIterator.next());
// Object {value: Array(3), done: false}
console.log(arrIterator.next());
// Object {value: undefined, done: true}
-
Array、Set、Map 都部署了下面的 3 个方法,调用后返回一个迭代器对象
- *.prototype.entries(): 返回一个迭代器对象,用来迭代 [键名, 键值] 组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用 entries 方法。
- *.prototype.keys(): 返回一个迭代器对象,用来迭代所有的键名。
- *.prototype.values(): 返回一个迭代器对象,用来迭代所有的键值。
生成器( Generator )
生成器是一种特殊的迭代器。所有的生成器都有内建 .next() 和 Symbol.iterator 方法的实现。
生成器函数在执行后会返回一个迭代器对象,即生成器。
-
生成器函数 VS 普通函数
-
普通函数使用
function声明,而生成器函数使用function*声明 - 在生成器函数内部,有一种类似 return 的语法: 关键字 yield 。二者的区别是,普通函数只可以 return 一次,而生成器函数可以 yield 多次(当然也可以只 yield 一次)。在生成器的执行过程中,遇到 yield 表达式立即暂停,后续可恢复执行状态。
-
普通函数使用
普通函数和生成器函数之间最大的区别,普通函数不能自暂停,生成器函数可以。
function*generator(){
yield 'hello';
yield 'vue.js';
yield {
name: 'ppp',
age: 22
};
}
let test = generator();
console.log(Object.prototype.toString.call(test)); // [object Generator]
console.log(test.next()); // Object {value: "hello", done: false}
console.log(test.next()); // Object {value: "vue.js", done: false}
console.log(test.next()); // Object {value: Object, done: false}
console.log(test.next()); // Object {value: undefined, done: true}
- yield * 语句
function*generator(){
yield 'hello';
yield* 'js';
}
let test = generator();
console.log(test.next()); // Object {value: "hello", done: false}
console.log(test.next()); // Object {value: "j", done: false}
console.log(test.next()); // Object {value: "s", done: false}
console.log(test.next()); // Object {value: undefined, done: true}
- 通过 next(value) 方法向 Generator 函数体内输入数据
function*gen(x){
let y = yield x + 2;
let z = yield y + 2;
console.log(x, y, z);
return x + y + z;
}
let g1 = gen(1);
g1.next(); // Object {value: 3, done: false}
g1.next(); // Object {value: NaN, done: false}
g1.next(); // Object {value: NaN, done: true}
// 生成器函数内部变量 x, y, z ---> 1 undefined undefined
let g2 = gen(1);
g2.next(); // Object {value: 3, done: false}
g2.next(2); // Object {value: 4, done: false}
g2.next(3); // Object {value: 6, done: true}
// 生成器函数内部变量 x, y, z ---> 1 2 3
通过 next(value) 方法向生成器函数传入的参数将作为上次 yield 语句的返回值。(g2.next(2) 的参数 2 将作为 yield x + 2
语句的返回值,然后赋值给变量 y)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 迭代器萃取与反向迭代器
- 浅谈python可迭代对象,迭代器
- 可迭代对象,迭代器(对象),生成器(对象)
- 终于把动态规划与策略迭代、值迭代讲清楚了
- 终于把动态规划与策略迭代、值迭代讲清楚了
- 搞清楚 Python 的迭代器、可迭代对象、生成器
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Caching
Duane Wessels / O'Reilly Media, Inc. / 2001-6 / 39.95美元
On the World Wide Web, speed and efficiency are vital. Users have little patience for slow web pages, while network administrators want to make the most of their available bandwidth. A properly design......一起来看看 《Web Caching》 这本书的介绍吧!