继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

JavaScript遍历器

为爱心太软
关注TA
已关注
手记 170
粉丝 1.4万
获赞 860

背景知识

在ES5 时代,如果想遍历数组中的数据,必须要初始化一个变量来记录每一次遍历在数组中的位置。

例子:

const arr = ["A", "B", "C"];
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);
}
//输出:A B C

截止到ES6,JavaScript 已经拥有了数组、对象、Map集合和Set集合这样四种数据结构。为了统一和简化遍历这四种数据结构的操作,ES6引入了遍历器机制

基本概念

ES6 规定,可遍历的对象都具有Symbol.iterator 属性,这个属性指向一个函数,就是当前对象默认的遍历器生成函数。这个遍历器生成函数大致的模样可以用ES5 语法模拟出来:这个函数返回一个next() 方法,每调用next() 方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value和done两个属性的对象。其中,value属性是当前成员的值,done属性是一个布尔值,表示遍历是否结束。

例子:

function createIterator(items) {
    let i = 0;
    return {
        next() {
            let done = i >= items.length;
            let value = !done ? items[i++] : undefined;
            return {
                done: done,
                value: value
            }
        }
    }
}

let iterator = createIterator([1, 2, 3]);
console.log(iterator.next()); //输出:{done: false, value: 1}
console.log(iterator.next()); //输出:{done: false, value: 2}
console.log(iterator.next()); //输出:{done: false, value: 3}
console.log(iterator.next()); //输出:{done: true, value:undefined }

默认遍历器

在ES6 中,已经默认为绝大多数內建的数据结构提供了遍历器,不需要像上面例子中那样自己去创建。默认的遍历器主要有以下三种:

1、keys()方法:默认遍历器,其值为集合中的所有键名。
2、values()方法:默认遍历器,其值为集合中的所有值。
3、entries()方法:默认遍历器,其值为所有成员的键值对。

例子:

const arr = ["A", "B", "C"];
console.log([...arr.keys()]); //输出:[0, 1, 2]
console.log([...arr.values()]); //输出:["A", "B", "C"]
console.log([...arr.entries()]); //输出:[[0, "A"],[1, "B"],[2, "C"]]

const set = new Set(arr);
console.log([...set.keys()]); //输出:["A", "B", "C"]
console.log([...set.values()]); //输出:["A", "B", "C"]
console.log([...set.entries()]); //输出:[["A", "A"],["B", "B"],["C", "C"]]

const map = new Map().set("name", "Tom").set("age", 19);
console.log([...map.keys()]); //输出:["name", "age"]
console.log([...map.values()]); //输出:["Tom", 19]
console.log([...map.entries()]); //输出:[["name", "Tom"],["age", 19]]

*扩展运算符(…)会持续调用遍历器的next() 方法,并将value属性值插入到数组中,直到结果对象的done 属性值为true。
*Set 集合的键名和键值是同一个值,所以keys() 方法和values() 方法的返回值完全一致。

不同数据结构的默认遍历器

每个数据结构都有一个默认的遍历器,例如数组的默认遍历器是values()
,在没有明确指定遍历器的情况下,这些数据结构都会使用默认的遍历器。我们可以通过检测对象的Symbol.iterator 属性来判断对象是否拥有遍历器。

例子:

const arr = ["A", "B", "C"];
console.log(typeof arr[Symbol.iterator] === "function"); //输出:true
console.log(arr[Symbol.iterator]); //输出:function values() { ... }

const set = new Set(arr);
console.log(typeof set[Symbol.iterator] === "function"); //输出:true
console.log(set[Symbol.iterator]); //输出:function values() { ... }

const map = new Map().set("name", "Tom").set("age", 19);
console.log(typeof map[Symbol.iterator] === "function"); //输出:true
console.log(map[Symbol.iterator]); //输出:function entries() { ... }

原生具备遍历器的对象有:数组、Map集合、Set集合、字符串、arguments和 NodeList(节点列表)。而对象(Object)默认是不可遍历的,我们可以通过Object.keys()、Object.values()和Object.entries() 方法把对象变成数组,使其拥有遍历器,或者直接为对象添加Symbol.iterator 属性来自定义遍历器。

例子1:

const obj = {
    name: "Tom",
    age: 19
}
console.log(typeof Object.entries(obj)[Symbol.iterator] === "function"); //输出:true
console.log([...Object.entries(obj)]); //输出:[["name", "Tom"],["age", 19]]

例子2:

const obj = {
    name: "Tom",
    age: 19,
    [Symbol.iterator]() {
        let data = [],
            i = 0;
        for (let k in this) {
            if (this.hasOwnProperty(k)) {
                data.push(Array.of(k, this[k]));
            }
        }
        return {
            next() {
                let done = i >= data.length;
                let value = !done ? data[i++] : undefined;
                return {
                    done: done,
                    value: value
                }
            }
        }
    }
}

console.log([...obj]); 
//输出:[["name", "Tom"],["age", 19]]

调用遍历器

当我们去遍历拥有遍历器的对象的时候,系统就会自动去调用对象默认遍历器的接口。没有遍历器接口的对象不能被遍历。

for…of 循环

我们知道,遍历器内部的next() 方法,每调用一次只会返回当前成员的信息,而for…of循环将持续调用next() 方法直到返回对象的done 属性的值为 true。

例子:

const arr = ["A", "B", "C"];
const map = new Map().set("name", "Tom").set("age", 19);
const str = "love";

for (let v of arr) {
    console.log(v); //输出:A B C
}

for (let [v, i] of map) {
    console.log(v + ":" + i); //输出:name:Tom age:19
}

for (let v of str) {
    console.log(v); //输出:l o v e
}

展开运算符

如果我们想把非数组可遍历对象的所有成员填充到一个数组中,最方便的方法是使用展开运算符(…)。展开运算符会触发默认的遍历器取得所有的值,然后按照取得顺序依次插入到数组中。

例子:

console.log([...new Set([1, 2, 3])]) //输出:[1, 2, 3]
console.log([1, ...new Set([2, 3]), ...
        "love"
    ]) //输出:[1, 2, 3, "l", "o", "v", "e"]

ES6中,由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合,其实都调用了遍历器。例如Array.from()方法、Set()、Map()、解构赋值等等。

例子:

console.log(Array.from(new Set([1, 2, 3]))); //输出:[1, 2, 3]
let [a, b] = new Set([1, 2]);
console.log(a + "," + b); //输出:1,2

如有错误,欢迎指正,本人不胜感激。

打开App,阅读手记
3人推荐
发表评论
随时随地看视频慕课网APP