ES6 迭代协议

栏目: JavaScript · 发布时间: 7年前

内容简介:ES6 迭代协议

ES6 中引入的迭代器和 Python 的迭代器类型非常类似,可互相参考进行学习。

迭代器贯穿 ES6 的始终,它是数据和循环的新标准。

迭代协议

  • 有两个迭代协议:可迭代协议和迭代器协议

    • 可迭代协议 允许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for..of 结构中什么值可以被循环(得到)
    • 迭代器协议 定义了一种标准的方式(调用 next() 方法)来产生一个有限或无限序列的值
  • 可迭代协议 —> 可迭代对象

  • 迭代器协议 —> 迭代器对象

可迭代对象

一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是可迭代的,即它是一种可迭代对象(iterable)。

ES6 规定,默认的 Iterator 接口部署在数据结构的 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().donetrue 结束。

  • 并不是所有的类数组对象都具有 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)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

娱乐至死

娱乐至死

[美] 尼尔·波兹曼 / 章艳 / 广西师范大学出版社 / 2011-6 / 29.80元

《娱乐至死》是对20世纪后半叶美国文化中最重大变化的探究和哀悼:印刷术时代步入没落,而电视时代蒸蒸日上;电视改变了公众话语的内容和意义;政治、宗教、教育和任何其他公共事务领域的内容,都不可避免的被电视的表达方式重新定义。电视的一般表达方式是娱乐。一切公众话语都日渐以娱乐的方式出现,并成为一种文化精神。一切文化内容都心甘情愿地成为娱乐的附庸,而且毫无怨言,甚至无声无息,“其结果是我们成了一个娱乐至死......一起来看看 《娱乐至死》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具