for…of循环_ES6笔记1

一.作用

用来遍历数组,类似于用for…in遍历对象

for (var i = 0; i < arr.length; i++)用着挺好,嫌麻烦还可以用arr.forEach(),那for...in存在的意义是什么?

  1. for (var i = 0; i < arr.length; i++)不够简洁

    太长,长就容易出错,比如笔者经常犯的错误:for (var j = 0; j < arr[i].length; i++)

  2. forEach不够灵活

    arr.forEach不好用,因为无法通过break或者return跳出

  3. for...in不适合用来遍历数组

    for…in会遍历到自定义属性甚至原型属性、index是字符串而不是数值、某些情况下甚至不按顺序遍历

所以,我们需要一种更方便的遍历数组的方法,就像用for...in遍历对象一样简单易用的方法,那就是for...of

二.特点

1.可以遍历其它集合

不仅可以遍历数组,还可以遍历其它集合(iterable),包括NodeList/arguments、字符串、Map、Set等等

示例代码如下:

// 数组
var arr = [1, 2, 2, 4];
for (var val of arr) {
    console.log(val);
}
// 类数组对象(arguments、NodeList)
(function() {
    for (var val of arguments) {
        console.log(val);
    }
})(1, 2, 4);
// Set
var uniqueArr = new Set(arr);   // Set自动去重
for (var val of uniqueArr) {
    console.log(val);
}
// Map
var map = new Map([['a', 1], ['b', 2]]);
map.set('c', 3);
for (var [key, val] of map) {
    console.log('map[' + key + '] = ' + val);
}

注意:Chrome47不支持var [key, val] of map语法,FF43支持,node --harmony v0.12.7不支持解构表达式。如果想痛快地体验ES6,建议node安装think.js

2.不能遍历对象

for...of不支持遍历对象,但可以通过添加[Symbol.iterator]()方法来让其它对象(比如jQuery对象)支持for...of遍历,有[Symbol.iterator]()方法的对象是可迭代的(iterable)

怎么个不支持法?示例代码如下:

var obj = {
    'a': 1,
    'b': 2
}

for (var val of obj) {
    console.log(val);
}
// Uncaught TypeError:  is not a function
// 报错,不支持遍历对象,那是for...in的职责

如果想让自定义对象支持for...of遍历,需要添加[Symbol.iterator]()方法,如下:

// 一般方法实现迭代器
class MyIterator {
    constructor(obj) {
        this.obj = obj;
        this.keys = Object.keys(obj);
        this.index = 0;
    }

    // 实现iterator接口
    [Symbol.iterator]() {
        return this;
    }
    next() {
        var val = this.obj[this.keys[this.index]];
        if (this.index === this.keys.length) {
            return {
                done: true
            }
        }
        else {
            this.index++;
            return {
                done: false,
                value: val
            }
        }
    }
}
// 测试
var _obj = {
    a: 2,
    b: 1
}
for (var val of new MyIterator(_obj)) {
    console.log(val);
}

等等,Symbol是什么东西?Symbol大有来头:Symbol是js中第7种基本类型(本来就有的6种是null, undefined, Number, Boolean, Object, String),不是字符串也不是对象,证据如下:

typeof Symbol() === 'symbol'

上面代码中出现的Symbol.iterator是一个内置的函数名(Symbol类型的,而一般的函数名是String),执行for...of语句的时候,解释器会在of后面的对象身上找名为Symbol.iterator的方法,该方法执行后返回一个迭代器对象iterator,拿到iterator后自然是.next().next()直到done,过程类似于:

// 数组
var arr = [1, 2, 2, 4];
// for (var val of arr) {
//     console.log(val);
// }
var iter = arr[Symbol.iterator]();
while (true) {
    var res = iter.next();
    if (res.done) {
        break;
    }
    else {
        var val = res.value;
        console.log(val);
    }
}

当然,我们这个功能太弱了,for...of的强大之处在于支持break, continue, return,就像经典for循环一样

3.支持自定义迭代器

其实上面我们已经实现了自定义迭代器,但好像很麻烦的样子。用这个,毋宁死。制定ES6标准的人自然也想到了,配合generator语法(function* + yield)很容易实现自定义迭代器,以支持for...of遍历:

var obj = {
    'a': 1,
    'b': 2
}
// generator实现迭代器
obj[Symbol.iterator] = function*() {
    for (var key in this) {
        yield this[key];
    }
}
for (var val of obj) {
    console.log(val);
}

秒秒钟实现一个迭代器,这下方便多了

P.S.其实我们的MyIterator严格来说是generator,(迭代器的)生成器

三.总结

ES5引入了forEach,但实际上没什么用,很多时候不得不把写好的forEach换成经典for循环。。forEach在灵活性方面不如every,但every会新建一个数组。。

好了,现在有了for...offorEach再见

参考资料

  • 《ES6 in Depth》:InfoQ中文站提供的免费电子书

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code