ES6 迭代协议
栏目: JavaScript · 发布时间: 7年前
内容简介: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 的迭代器、可迭代对象、生成器
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。