博客
电影
宝箱
友链
关于
<
2018年个人总结,困知勉行
《居里夫人自传》读后感
>
ES6迭代器Iterator原理与性能
作者:
Cifer
类别: 技术·JS
时间:2018-12-28 22:05:58
字数:6411
版权所有,未经允许,请勿转载,谢谢合作~
#### 目录 <a href="#t1">1. 为什么需要迭代器Iterator</a> <a href="#t2">2. 迭代器Iterator实现</a> <a href="#t3">3. 生成器Generator</a> <a href="#t4">4. 哪些数据结构默认实现了Iterator</a> <a href="#t5">5. for in, for of 遍历属性的区别</a> <a href="#t6">6. 可枚举enumerable对for in, for of的影响</a> <a href="#t7">7. for,for in, for of性能比较</a> <i id="t1"></i> #### 为什么需要迭代器Iterator ES6以前,在for循环下,一般是使用普通遍历与for in遍历数组: ```javascript let arr = [111, 222]; for (let i = 0, len = arr.length;i < len;i++) { console.log(arr[i]); } for (let k in arr) { console.log(arr[k]); } ``` 而迭代器为更多的数据结构提供了迭代通用接口,每次遍历都调用next(), 并统一返回结果对象(内含value当前结果与done (Boolean)是否遍历完成),简化了迭代过程。而所有实现了迭代器的数据结构所创建的对象,又都支持for of,它会自动调用next()且返回结果,遍历完成后自动中止。 ```javascript for (let v of arr) { console.log(v); } ``` 与上述打印结果是一致的。 <i id="t2"></i> #### 迭代器Iterator实现 迭代器如果需要满足各种数据结构,实现过程其实是比较复杂的,为了便于理解,我们只看Array的情况: ```javascript function arrayIterator(items) { if (items && items.length >= 0) { let i = 0; const len = items.length; return { next: function() { return { done: i >= len, value: i < len ? items[i++] : undefined, } } } } } let arr = [111, 222]; let arrIterator = arrayIterator(arr); console.log(arrIterator.next()); // {done: false, value: 111} console.log(arrIterator.next()); // {done: false, value: 222} console.log(arrIterator.next()); // {done: true, value: undefined} ``` <i id="t3"></i> #### 生成器Generator 生成器Generator是返回迭代器Iterator的函数, 用星号标示,函数内部使用yield (至少1个)打上每次next()调用时的“断点”,还可用在异步操作,return会打断后面的next, 但本身不会占用“断点”次数。 ```javascript function *generatorIterator(arr) { for(let i = 0, len = arr.length;i < len;i++) { yield arr[i]; } } let arr = [11, 22]; let itera = generatorIterator(arr); console.log(itera.next()); // {done: false, value: 11} console.log(itera.next()); // {done: false, value: 22} console.log(itera.next()); // {done: true, value: undefined} ``` <i id="t4"></i> #### 哪些数据结构默认实现了Iterator 所有拥有拥有Symbol.iterator 方法的数据结构所创建的对象都是可迭代,比如Array, Set, Map, String,页面NodeList等。 ```javascript typeof new Array()[Symbol.iterator] === 'function' // true typeof new String()[Symbol.iterator] === 'function' // true typeof new Set()[Symbol.iterator] === 'function' // true typeof new Map()[Symbol.iterator] === 'function' // true typeof document.getElementsByTagName('html')[Symbol.iterator] === 'function' // true typeof new Object()[Symbol.iterator] === 'function' // false typeof new WeakSet()[Symbol.iterator] === 'function' // false typeof new WeakMap()[Symbol.iterator] === 'function' // false typeof new Number()[Symbol.iterator] === 'function' // false typeof new Boolean()[Symbol.iterator] === 'function' // false typeof new Function()[Symbol.iterator] === 'function' // false typeof new Date()[Symbol.iterator] === 'function' // false ``` 可以看到Object创建的对应默认是不可迭代的,但可以修改Symbol.iterator冒充实现。 ```javascript let obj = { items: [], *[Symbol.iterator]() { for (let i of this.items) { yield i; } } } obj.items = [11, 22]; for (let i of obj) { console.log(i); } // 11 // 22 typeof obj[Symbol.iterator] === 'function' // true ``` 如果想同时使用next(),则还需要加上述迭代器实现的代码。 ```javascript let obj = { items: [], *[Symbol.iterator]() { for (let i of this.items) { yield i; } }, i: 0, next: function() { let len = this.items.length; return { done: this.i >= len, value: this.i < len ? this.items[this.i++] : undefined, } } } obj.items = [11, 22]; console.log(obj.next()); // {done: false, value: 11} console.log(obj.next()); // {done: false, value: 22} console.log(obj.next()); // {done: true, value: undefined} for (let i of obj) { console.log(i); } // 11 // 22 ``` <i id="t5"></i> #### for in, for of 遍历属性的区别 for in遍历默认是key,并且会遍历所有的属性。而for of遍历的默认是value,只会遍历其自有属性。 ```javascript Array.prototype.extraProperty = 'extra'; let arr = ['a']; for (let k in arr) { console.log(arr[k]); } // a // extra for (let v of arr) { console.log(v); } // a ``` 如果要使用for in实现for of的效果,可以使用hasOwnProperty先判断。 ```javascript for (let k in arr) { if (arr.hasOwnProperty(k)) { console.log(arr[k]); } } ``` <i id="t6"></i> #### 可枚举enumerable对for in, for of的影响 ```javascript Array.prototype.extraProperty = 'extra'; let arr = ['a']; Object.defineProperty(Array.prototype, 'extraProperty', { enumerable: false, }); Object.defineProperty(arr, 0, { enumerable: false, }); for (let k in arr) { console.log(arr[k]); } // 无打印 for (let v of arr) { console.log(v); } // a ``` 说明for in 可识别原型与对象本身的 enumerable。 for of 不可识别对象enumerable。 因其本身就不遍历原型的属性,是否可识别原型enumerable 也就无法确定,不过我们可以换个角度来看: ```javascript Array.prototype.extraProperty = 'extra'; for (let v of Array.prototype) { console.log(v); } // 无打印 console.log(JSON.parse(JSON.stringify(Object.values(Array.prototype)))); // ["extra"] Object.defineProperty(Array.prototype, 'extraProperty', { enumerable: false, }); console.log(JSON.parse(JSON.stringify(Object.values(Array.prototype)))); // [] ``` for of 无法拿到原型的属性。Object.values() 作用是返回对象可枚举属性的数组,即可识别 enumerable , 而for in是遍历所有属性同时可识别enumerable。 有大概率猜测出for of 普通对象enumerable都无法识别,原型属性就更不能了(只是猜测,但其实不重要,因为没有属性,属性下所有的一切也就没有意义)。 <i id="t7"></i> #### for,for in, for of性能比较 三者时间分别是40.6ms, 255.9ms, 51.9ms,可见for of性能接近普通 for, 而for in则差了一个数量级。测试代码如下: ```javascript // 这么写的目的,使得每次花费时间都不因太小而变为0,提高精度。 function doSomething(v) { let temp; temp = (v + 8)%88 / 888 + 8888 - 7777 + 6666 - 5555 + 4444 - 3333 + 2222 % 111 / 11 + 1; temp = (temp + 8)%88 / 888 + 8888 - 7777 + 6666 - 5555 + 4444 - 3333 + 2222 % 111 / 11 + 1; temp = (temp + 8)%88 / 888 + 8888 - 7777 + 6666 - 5555 + 4444 - 3333 + 2222 % 111 / 11 + 1; return temp } function normalFor(arr) { let temp; for (let i = 0, len = arr.length;i < len;i++) { temp = doSomething(arr[i]); } return temp; } function forIn(arr) { let temp; for (let k in arr) { temp = doSomething(arr[k]); } return temp; } function forOf(arr) { let temp; for (let v of arr) { temp = doSomething(v); } return temp; } //计算时间例子,分别是每次的计算的开始时间与结束时间 var d1,d2; //随机生成数组元素 function getArr(){ var arr = []; for(var i = 0;i < 1000000;i++){ arr.push(Math.floor(Math.random()*10000)); } return arr; } //获取时间 function getTime(func){ //保存每次计算的时间,如果出现0则可能误差较大 var arrTime = []; //计算10次,取平均值,减小误差 for(var k = 0;k < 10;k++){ var arr = getArr(); d1 = new Date().getTime(); //使用上述算法 func(arr); d2 = new Date().getTime(); arrTime.push(d2 - d1); } var all = 0; for(var m = 0;m < arrTime.length;m++){ all = all + arrTime[m]; } console.log(arrTime); console.log(all/arrTime.length); } getTime(normalFor); // [48, 41, 38, 40, 39, 40, 40, 40, 39, 41] // 40.6 getTime(forIn); // [245, 306, 223, 236, 231, 239, 260, 265, 240, 314] // 255.9 getTime(forOf); // [80, 53, 49, 47, 53, 48, 47, 47, 49, 46] // 51.9 ```
如果觉得有帮忙,您可以在本页底部留言。
相关推荐:
NPM发包流程与技巧
Puppeteer爬取豆瓣电影TOP250评分
基于vue实现腾讯云点播的上传与播放
JS代理Proxy与反射Reflect场景
轻应用PWA实践全过程
JavaScript之Set与Map
ES6设计模式之观察者模式
解决toFixed四舍五入陷阱
深入理解IEEE754的64位双精度
ES6设计模式之单例模式
ES6设计模式之工厂模式
ES6二叉树的实现
JavaScript链表实例
JavaScript排序算法及性能比较
原生ajax及jQuery封装ajax实例
JavaScript类的语法糖
……
更多
<
2018年个人总结,困知勉行
《居里夫人自传》读后感
>
全部留言
我要留言
内容:
网名:
邮箱:
个人网站:
发表
全部留言