前言
在我们编写程序的过程中,遍历是常有的需求,如果对应的集合较小,不同遍历的方式之间的差距可能不会很大,可如果对于一个大型集合来说,选择对的遍历方法就至关重要了,尤其是对于浏览器这种对于时间及其敏感的环境。
本篇文章将会针对JavaScript中数组和对象这两种基本的数据结构的各个内置遍历方法进行分析。
遍历方式
- 数组遍历方式
- for 循环
- for 循环且缓存数组长度
- forEach
- while循环
- for-in
- for-of
- 对象的遍历方式
- for-in
- Object.keys + for循环
- Object.keys + for-of
- Object.keys + forEach
测试代码
function getRunTime(func) {const now = performance.now()func()return performance.now() - now}var arr = createArrayWithLen(1000000)var obj = createObjWithLen(100000)var obj1 = createObjWithLen(10000)Object.setPrototypeOf(obj, obj1)function createObjWithLen(len) {const obj = {}while(len) {obj[Math.random()] = lenlen--}return obj}function createArrayWithLen(len) {const arr = []while(len--) {arr.push(Math.random())}return arr}function objForIn() {ret = 0for (let i in obj) {if (!obj.hasOwnProperty(i)) continueret += obj[i]}}function objKeysForLoop() {ret = 0const keys = Object.keys(obj)for (let i = 0; i < keys.length; i++) {ret += obj[keys[i]]}}function objKeysForOf() {ret = 0for (let key of Object.keys(obj)) {ret += obj[key]}}function objKeysForEach() {ret = 0const keys = Object.keys(obj)keys.forEach(key => {ret += obj[key]})}function forLoop() {ret = 0for (let i = 0; i < arr.length; i++) {ret += arr[i]}}function forOf() {ret = 0for (let i of arr) {ret += i}}function forLoopWithCache() {ret = 0for (let i = 0, len = arr.length; i < len; i++) {ret += arr[i]}}function whileLoop() {ret = 0let i = 0while (i < arr.length) {ret += arr[i++]}}function forEach() {ret = 0arr.forEach(item => {ret += item})}function forIn() {ret = 0for (let i in arr) {if (!obj.hasOwnProperty(i)) continueret += arr[i]}}function average(times, func, ...args) {let ret = 0let len = timeswhile(len) {ret += func(...args)len--}return ret / times}function runTest(options) {const result = Object.keys(options).reduce((acc, key) => {acc[key] = average(10, getRunTime, options[key])return acc}, {})console.table(result)}runTest({forLoop,forLoopWithCache,whileLoop,forOf,forEach,forIn,objForIn,objKeysForLoop,objKeysForOf,objKeysForEach})
结论
| 单个测试进行十次取平均值 | |
|---|---|
| 数组遍历(1000000个元素) | 时间(ms) |
| forLoop | 11.090499999772874 |
| forLoopWithCache | 10.3529999996681 |
| whileLoop | 10.647999999855529 |
| forOf | 20.10000000045693 |
| forEach | 25.5765000001702 |
| forIn | 243.2185000008758 |
| 对象遍历(100000个键值对+1000键值对的原型) | 时间(ms) |
| objForIn | 37.153500000567874 |
| objKeysForLoop | 20.723500000167405 |
| objKeysForOf | 19.997999999759486 |
| objKeysForEach | 20.322000000305707 |
- 对于数组遍历来说:for≈while < for-of < forEach << for-in。
- for循环无论缓不缓存数组长度影响不大
- forEach的遍历性能比不上普通的for和while循环,不过差距不大
- 避免使用for-in遍历数组,由于需要遍历原型链,性能极差
- 对于对象来说:
- 对于普通对象来说,四者区别不大
- 对于原型不是Object.prototype的对象,由于需要遍历原型链,for-in的性能比起Object.keys遍历性能会较为差一点
