一.认识Iterator对象(可遍历对象)


        console.log([1, 2]);
        console.log([1, 2][Symbol.iterator]);//ƒ values() { [native code] }
        console.log([1, 2]['Symbol.iterator']);//undefined 注意这个特殊不能加''
        console.log([1, 2]['reverse']);//ƒ reverse() { [native code] }
        console.log(Symbol.iterator);//Symbol(Symbol.iterator)
   

Symbol是ES6新增函数,这里是一个对象,一切皆对象.函数也可以添加属性和方法;

Symbol.iterator是这个数组原型上面的方法,这个数组可以使用他的方法;

        // console.log([1, 2][Symbol.iterator]);//ƒ values() { [native code] }
console.log([1, 2][Symbol.iterator]());//Array Iterator {} 得到的是该数组的Iterator对象(可遍历/迭代对象);这个对象的原型上有next方法:

二。使用Iterator

        const it = [1, 2][Symbol.iterator]();
console.log(it.next());//{value: 1, done: false}
console.log(it.next());//{value: 2, done: false}
console.log(it.next());//{value: undefined, done: true}

value的1是数组的数组元素1;done属性表示遍历是否完成;把数组里面的成员都遍历完再遍历一次value得到undefined才算遍历完成;

1.可遍历对象:不是任何对象都叫可遍历对象,首先要能使用next方法,且该对象调用next方法返回一个对象,这个对象有value和done属性;

2.Symbol.iterator是可遍历对象生成方法  : [1, 2][Symbol.iterator]() ---->得到该数组的可遍历对象:Array Iterator {} ------>利用next方法遍历数组;

三。什么是iterator: [1, 2][Symbol.iterator]() ---->得到该数组的可遍历对象:Array Iterator {} ------>利用next方法遍历数组,这个过程就是iterator

四。为什么要用iterator遍历方式:因为是统一的遍历方式,下面要讲的for-of循环的底层原理就是使用iterator遍历方式;

五。for-of

1.底层原理

        const arr = [1, 2, 3];
const it = arr[Symbol.iterator]();
let next = it.next()
while (!next.done) {
console.log(next.value);// 1 2 3
next = it.next();
}

所以数组之所以可以用for of循环,是因为他可以使用可遍历对象生成方法symbol.iterator

2。使用for-of

        const arr = [1, 2, 3];
console.log(arr);
for (const item of arr) {
console.log(item);// 1 2 3
};

①.在for-of中获得数组的索引

arr.keys()得到的就是可遍历对象

arr.keys()和数组一样,他也可以调用可遍历对象生成方法,所以arr.keys()也可以用for of循环,arr.keys()也可以像arr一样写在of后面;

keys()是对键的遍历,这里的键是索引

②.在for-of中获得数组的值,和for-of数组本身没区别

values()是对值的遍历,和直接of数组本身没区别;

③.在for-of中获得数组每一个成员的下标及值组合而成的数组

entries()是对键值对的遍历

第一轮循环const entries =[0,1]------->{const entries=[0,1]...}

第二轮循环{const entries =[1,2]...}

解构赋值

第一轮循环const  [index,value] = [ 0,1]------->{const  [index,value]=[0,1]...}

第二轮循环{const  [index,value] =[1,2]...}

注意:for each 方法没办法和break和continue使用;因为for each是方法不是for循环;

六。原生可遍历 与 非原生可遍历

1.只要有 Symbol.iterator 方法,并且这个方法可以生成可遍历对象,就是可遍历的;

2.数组天生就可以调用Symbol.iterator 方法,生成可遍历对象,所以是原生可遍历;普通对象天生不能调用Symbol.iterator 方法,可以通过自己添加这个方法,达到可遍历,所以通过添加的方式后就是非原生可遍历

3.只要可遍历,就可以使用 for...of 循环来对其统一遍历(该数据调用Symbol.iterator 方法返回可遍历对象,这个对象可以使用next方法,且调用next方法返回一个对象有value和done属性

4.原生可遍历有哪些:

5.原生可遍历的有哪些:数组、字符串、Set、Map、arguments、NodeList
        // for (const item of [1, 2, 3]) {
// console.log(item);//1 2 3
// }
// for (const item of 'hi') {
// console.log(item);//'h' 'i'
// }
// for (const item of new Set([1, 2])) {
// console.log(item);//1 2
// }
for (const item of new Map([['a', 1], ['b', 2], ['a', 3]])) {
console.log(item);// ['a', 3] ['b', 2]
}

6:非原生可遍历:

①一般的对象,我们手动添加可遍历对象生成方法Symbol.iterator,且调用该方法要让他返回可遍历对象(首先要能使用next方法,且该对象调用next方法返回一个对象,这个对象有value和done属性;

我们手动添加一下:(先留一个问题:既然是手动添加,那么必须用Symbol.iterator、next、value,done这些命名吗?)只要这个对象满足obj[Symbol.iterator]()返回的是可遍历对象(首先要能使用next方法,且该对象调用next方法返回一个对象,这个对象有value和done属性;


        const obj = {
            a: 1,
            b: 2,
            [Symbol.iterator]() {//注意,不能写成'Symbol.iterator',否则无法使用for-of
                let index = 0
                return {//返回的是可遍历对象
                    next() {
                        index++;
                        if (index === 1) {
                            return {
                                value: obj.a,
                                done: false
                            };
                        } else if (index === 2) {
                            return {
                                value: obj.b,
                                done: false
                            };
                        } else {
                            return {
                                //value: undefined,//可以不写就相当于undefined
                                done: true
                            };
                        }
                    }
                };
            }
        }
        //for of底层原理
        const it = obj[Symbol.iterator]();
        console.log(it);
        let next = it.next();
        while (!next.done) {
            console.log(next.value);
            next = it.next();
        };
        //添加可遍历对象生成方法后就可以对obj用for-of
        for (const item of obj) {
            console.log(item);//1 2
        }
 

//这里涉及闭包,index局部变量没有被销毁

②有 length 和索引属性的对象

第一种方法:我们手动添加一下可遍历对象生成方法

        const obj = {
0: 'a',
1: 'b',
length: 2
}
obj[Symbol.iterator] = function () {
let index = 0;
return {
next() {
let value, done;
if (index < obj.length) {
value = obj[index];
done = false;
} else {
value = undefined;
done = true;
}
index++;
return {
value,
done
}
}
}
}
for (const item of obj) {
console.log(item);//'a' 'b'
}
//由此可知,数组的可遍历对象生成方法底层大概逻辑就是如此

第二种方法,由于这种对象类似类数组对象,有 length 和索引属性,可以直接

  obj[Symbol.iterator] = Array.prototype[Symbol.iterator];
 
七。使用iterator的场合,针对可遍历的数据,能调用可遍历对象生成方法才谈得上使用iterator
1。数组展开运算符,只要是可遍历的,就可以按照数组的方式直接展开(可以直接...数据)
        console.log(...[1, 2, 3]);
console.log(1, 2, 3);
console.log(...'str');
console.log(...new Set([1, 2, 3]));
// console.log(...{}); ×
const m = new Map([['a', 1], ['b', 2], ['a', 3]]);
console.log(...m);//['a', 3]  ['b', 2]
注意:对象的展开运算符原理不是iterator
①因为没有添加可遍历对象生成方法的对象是不可遍历的,所以不能按照数组的方式直接展开
        const obj1 = {
a: 1,
b: 2
}
console.log(...obj1);//报错

②当然手动加了可遍历对象生成方法的对象(非原生可遍历)可以按照数组的形式直接展开

        const obj = {
0: 'a',
1: 'b',
length: 2
}
obj[Symbol.iterator] = function () {
let index = 0;
return {
next() {
let value, done;
if (index < obj.length) {
value = obj[index];
done = false;
} else {
value = undefined;
done = true;
}
index++;
return {
value,
done
}
}
}
}
console.log(...obj);// 'a' 'b'

2.数组的解构赋值,只要是可遍历的,就可以按照数组的方式解构赋值,即使结构不匹配,但是程序内部会先把解构右边的数据先在数组中展开,再解构

3.Set 和 Map 的构造函数的实参必须满足是可遍历数据,当然Map的实参还须满足体现出键值对;

最新文章

  1. SAMEORIGIN
  2. BestCoder Round #79 (div.2)
  3. 关于显示gif的一些方法与讨论
  4. JAVA三大框架的各自作用
  5. [Angular 2] Build a select dropdown with *ngFor in Angular 2
  6. 一个AVRUSB作品HID类
  7. 多线程11_张孝祥 java5的线程锁技术
  8. cocos2d学习笔录1
  9. NuGet(Nuget Packages)
  10. git分支管理之Feature分支
  11. 搭建Google镜像网站
  12. js中 给json对象添加属性和json数组添加元素
  13. springboot启动配置原理之三(事件监听机制)
  14. javascript 跨域 的几种方法
  15. Asp.Net Core基于Cookie实现同域单点登录(SSO)
  16. Oracle数据库修改LISTENER的监听端口
  17. centos7 rabbitmq集群搭建+高可用
  18. vue webpack 打包后css背景图路径问题
  19. EMS_PM_STORAGE
  20. libpcap详解【转】

热门文章

  1. Cesium计算三角形面积(十)
  2. 【译】.NET 7 中的性能改进(五)
  3. C#计时器 Stopwatch 使用demo
  4. python基础语法图
  5. vue2 使用x2js json转换成xml
  6. WeNet调试
  7. Kmeans中文聚类
  8. Linux服务器监控性能测试
  9. GBDT初识
  10. 1839:【05NOIP提高组】谁拿了最多奖学金