ES6 中 for of 和迭代器 学习笔记

发布于 2022-07-09 16:22:44 字数 2487 浏览 1118 评论 5

相比 ES3 到 ES5,ES5 到 ES6 是更重大的升级,既有大量语法糖,如 Arrow FunctionTemplate string 等等,更有模块化、classgenerator 等等强大的新特性。相信 ES6 会极大的改变我们编写 JS 的方式,而且 ES6(ES2015)已经在2015年6月17日发布,所以说可以开始学习 ES6 了。

本文是我学习ES6的笔记,大部分是概念摘要,代码演示等等,以弄清概念为要。

另,学习资料来源(会及时更新):es6-in-depth

ES5中,forEach 可以用来遍历数组元素,但它的缺陷是不能使用break语句中断循环,也不能使用return语句返回到外层函数。

强大的 for-of 循环

for (let value of [1, 2, 3]) {
  console.log(value); //输出 1 2 3
}
  • 最简洁、最直接的遍历数组元素的语法
  • 这个方法避开了 for-in 循环的所有缺陷
  • forEach() 不同的是,它可以正确响应 breakcontinue 和 return 语句

其它集合也支持for-of循环

for-of 循环不仅支持数组,还支持大多数类数组对象,例如 DOM 的 NodeList 对象。它也支持字符串:

for (let chr of "abc12") {
    console.log(chr); // 输出 "a" "b" "c" "1" "2"
}

另外还支持MapSet对象的遍历。

深入理解

正如其它语言中的for/foreach语句一样,for-of循环语句通过方法调用来遍历各种集合。数组、Map、Set以及我们讨论的其它对象有一个共同点,它们都有一个迭代器方法。

任何对象都可以有/添加迭代器方法。

就像为对象添加myObject.toString()方法,JS知道怎么把这个对象转化为字符串;你为对象添加迭代器方法myObject[Symbol.iterator](),JS也就知道了如何遍历这个对象。

[Symbol.iterator]语法看起来很怪。Symbol是ES6引入的新类型,标准定义了全新的symbol(如 Symbol.iterator),来保证不与任何已有代码产生冲突。

任何有迭代器方法[Symbol.iterator]()的对象都是可迭代的。

迭代器对象

迭代器方法 [Symbol.iterator]() 返回一个迭代器对象。迭代器对象可以是任何有 next() 方法的对象。for-of 循环将重复调用这个方法。

最简单的迭代器对象:

var zeroesForeverIterator = {
    [Symbol.iterator]: function() {
        return this;
    },
    next: function() {
        return {
            done: false,
            value: 0
        };
    }
};

每一次调用 next() 方法,它都返回相同的结果,告诉 for-of 循环:(a) 我们尚未完成迭代;(b)下一个值为0。这意味着 for (value of zeroesForeverIterator) {} 是一个无限循环。当然,一般来说迭代器不会如此简单。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

诠释孤独 2022-05-04 13:58:59

对数组来讲,请用 yield * array,比如:

function* test() {
  yield* [1,2,3].map(v => {
    return v;
  });
}

var gen = test();
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());
console.log(gen.next());

/*
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: undefined, done: true }
*/
缪败 2022-05-04 13:58:56
//比如下面这样的内容
let list=['/dev/text01.text','/dev/text02.text'];

function* readFile(){
  list.forEach(function(dir){

    yield fs.readFile ........ // 或者 这个readFile整个用co类库包裹一下。 因为 这个回调是一个普通的function导致,这里的yield直接报错了,这里是不是这能用for of/ for循环去解决,不能用之前的forEach了?

  });
}
过潦 2022-05-04 13:57:03

@skyujilong generator也是iterator,你可以用for of代替forEach。具体可以看开头部分。

已下线请稍等 2022-05-04 13:48:44

生成器如何配合 类似这种forEach 函数?

橙味迷妹 2022-05-03 22:54:31

generator(生成器)

生成器是ES6最具魔力的特性。为什么这么说?对初学者来说,它与JS已有的特性截然不同,可能第一眼会觉得晦涩难懂。但是,从某种意义上来说,它使语言内部的常规行为变得更加强大。如果这都不是魔力,我不知道还有什么是。

注意:生成器可以简化代码,解决回调地狱。

生成器简介

什么是生成器?从一个示例开始:

function* quips(name) {
    yield "hello " + name + "!";
    yield "i hope you are enjoying the blog posts";
    if (name.startsWith("X")) {
        yield "it's cool how your name starts with X, " + name;
    }
    yield "see you later!";
}

它看起来有点像函数,对吗?它叫生成器函数(generator-function),和函数在很多方面一致。但有两处不同:

  • function <-> function*
  • 在生成器函数内,yield是关键词,语法和return相似。区别在,函数(包括生成器函数)只能return一次,生成器函数可以yield任意次数。yield表达式挂起生成器的执行,所以后面可以恢复。

好,这就是很大的不同:常规函数不能暂停自己,但生成器函数可以。

生成器做了什么

当你调用quips()生成器函数时发生了什么?

> var iter = quips("jorendorff");
  [object Generator]
> iter.next()
  { value: "hello jorendorff!", done: false }
> iter.next()
  { value: "i hope you are enjoying the blog posts", done: false }
> iter.next()
  { value: "see you later!", done: false }
> iter.next()
  { value: undefined, done: true }

普通函数,当你调用它们时,它们立即运行,直到遇到return或抛出异常时才退出执行。

调用生成器函数看起来类似:quips("jorendorff")。但当你调用生成器函数时,它并没有开始运行。相反,它返回一个生成器(Generator)对象(上面的iter)。你可以将这个生成器对象视为一次函数调用,只不过立即冻结了,它恰好在生成器函数的最顶端(第一行代码之前)冻结了。

每次你调用生成器对象的.next()方法时,函数调用将其自身解冻并一直运行到下一个yield表达式前,再次暂停。

调用最后一个iter.next()时,我们最终抵达生成器函数的末尾,所以返回结果中done的值为true。抵达函数的末尾就像return undefined,所以返回结果中value的值为undefined

用专业术语来说,每当生成器执行yields语句,生成器的堆栈帧(stack frame,含有本地变量、参数、临时值、生成器内部当前的执行位置)被移出堆栈。但是,生成器对象保留了对这个帧的引用(或复制),所以稍后调用.next()可以重新激活帧并且继续执行。

注意:生成器不是线程

生成器可以暂停,恢复运行。那么问题来了,这有什么用?

生成器是迭代器

结合上一部分的迭代器,我们来构造一个range迭代器,就简单地从一个数增加到另一个数。

// This should "ding" three times
for (var value of range(0, 3)) {
    console.log("Ding! at floor #" + value);
}
// 输出
//Ding! at floor #0
//Ding! at floor #1
//Ding! at floor #2
class Range{
    constructor(start, stop) {
        this.value = start;
        this.stop = stop;
    }
    [Symbol.iterator]() {return this;}
    next() {
        var value = this.value;
        if(value < this.stop) {
            this.value++;
            return {done: false, value: value};
        } else {
            return {done: true, value: undefined};
        }
    }
}

function range(start, stop) {
    return new Range(start, stop);
}

上面的实现看起来像Java或Swift,不坏,但也不简洁。看起来迭代器很好用,但实现起来有点麻烦。

有更好的方式吗?用生成器:

function* range(start, stop) {
    for (var i = start; i < stop; i++)
        yield i;
}

4行代替23行,之所以可行,因为生成器就是迭代器(generators are iterators)。所有的生成器都内置.next()[Symbol.iterator]()的实现。你只要写循环行为。

如何使用生成器作为迭代器的能力?

  • 使任意对象可迭代。编写生成器函数遍历这个对象,运行时yield每一个值。然后将这个生成器函数作为这个对象的[Symbol.iterator]方法。
  • 简化数组构建函数。比如:
// Divide the one-dimensional array 'icons'
// into arrays of length 'rowLength'.
function splitIntoRows(icons, rowLength) {
    var rows = [];
    for (var i = 0; i < icons.length; i += rowLength) {
        rows.push(icons.slice(i, i + rowLength));
    }
    return rows;
}

可以变成:

function* splitIntoRows(icons, rowLength) {
    for (var i = 0; i < icons.length; i += rowLength) {
        yield icons.slice(i, i + rowLength);
    }
}
~没有更多了~

关于作者

轮廓§

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

小瓶盖

文章 0 评论 0

wxsp_Ukbq8xGR

文章 0 评论 0

1638627670

文章 0 评论 0

仅一夜美梦

文章 0 评论 0

夜访吸血鬼

文章 0 评论 0

近卫軍团

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文