共计 7323 个字符,预计需要花费 19 分钟才能阅读完成。
这是第 100 篇不掺水的原创,想获取更多原创好文,请搜寻公众号关注咱们吧~ 本文首发于政采云前端博客:JS 罕用的循环遍历你会几种
前言
数组和对象作为一个最根底数据结构,在各种编程语言中都充当着至关重要的角色,你很难设想没有数组和对象的编程语言会是什么模样,特地是 JS,弱类型语言,非常灵活。本文带你理解罕用数组遍历、对象遍历的应用比照以及注意事项。
数组遍历
随着 JS 的一直倒退,截至 ES7 标准曾经有十多种遍历办法。上面依照性能相似的办法为一组,来介绍数组的罕用遍历办法。
for、forEach、for …of
const list = [1, 2, 3, 4, 5, 6, 7, 8,, 10, 11];
for (let i = 0, len = list.length; i < len; i++) {if (list[i] === 5) {
break; // 1 2 3 4
// continue; // 1 2 3 4 6 7 8 undefined 10 11
}
console.log(list[i]);
}
for (const item of list) {if (item === 5) {
break; // 1 2 3 4
// continue; // 1 2 3 4 6 7 8 undefined 10 11
}
console.log(item);
}
list.forEach((item, index, arr) => {if (item === 5) return;
console.log(index); // 0 1 2 3 5 6 7 9 10
console.log(item); // 1 2 3 4 6 7 8 10 11
});
小结
- 三者都是根本的由左到右遍历数组
- forEach 无奈跳出循环;for 和 for ..of 能够应用 break 或者 continue 跳过或中断。
- for …of 间接拜访的是理论元素。for 遍历数组索引,forEach 回调函数参数更丰盛,元素、索引、原数组都能够获取。
- for …of 与 for 如果数组中存在空元素,同样会执行。
some、every
const list = [{ name: '头部导航', backward: false},
{name: '轮播', backward: true},
{name: '页脚', backward: false},
];
const someBackward = list.some(item => item.backward);
// someBackward: true
const everyNewest = list.every(item => !item.backward);
// everyNewest: false
小结
- 二者都是用来做数组条件判断的,都是返回一个布尔值
- 二者都能够被中断
- some 若某一元素满足条件,返回 true,循环中断;所有元素不满足条件,返回 false。
- every 与 some 相同,若无益元素不满足条件,返回 false,循环中断;所有元素满足条件,返回 true。
filter、map
const list = [{ name: '头部导航', type: 'nav', id: 1},,
{name: '轮播', type: 'content', id: 2},
{name: '页脚', type: 'nav', id: 3},
];
const resultList = list.filter(item => {console.log(item);
return item.type === 'nav';
});
// resultList: [// { name: '头部导航', type: 'nav', id: 1},
// {name: '页脚', type: 'nav', id: 3},
// ]
const newList = list.map(item => {console.log(item);
return item.id;
});
// newList: [1, empty, 2, 3]
// list: [// { name: '头部导航', type: 'nav', id: 1},
// empty,
// {name: '轮播', type: 'content', id: 2},
// {name: '页脚', type: 'nav', id: 3},
// ]
小结
- 二者都是生成一个新数组,都不会扭转原数组(不包含遍历对象数组是,在回调函数中操作元素对象)
- 二者都会跳过空元素。有趣味的同学能够本人打印一下
- map 会将回调函数的返回值组成一个新数组,数组长度与原数组统一。
- filter 会将合乎回调函数条件的元素组成一个新数组,数组长度与原数组不同。
- map 生成的新数组元素是可自定义。
- filter 生成的新数组元素不可自定义,与对应原数组元素统一。
find、findIndex
const list = [{ name: '头部导航', id: 1},
{name: '轮播', id: 2},
{name: '页脚', id: 3},
];
const result = list.find((item) => item.id === 3);
// result: {name: '页脚', id: 3}
result.name = '底部导航';
// list: [// { name: '头部导航', id: 1},
// {name: '轮播', id: 2},
// {name: '底部导航', id: 3},
// ]
const index = list.findIndex((item) => item.id === 3);
// index: 2
list[index].name // '底部导航';
小结
- 二者都是用来查找数组元素。
- find 办法返回数组中满足 callback 函数的第一个元素的值。如果不存在返回 undefined。
- findIndex 它返回数组中找到的元素的索引,而不是其值,如果不存在返回 -1。
reduce、reduceRight
reduce 办法接管两个参数,第一个参数是回调函数(callback),第二个参数是初始值(initialValue)。
reduceRight 办法除了与 reduce 执行方向相同外(从右往左),其余齐全与其统一。
回调函数接管四个参数:
- accumulator:MDN 上解释为累计器,但我感觉不失当,按我的了解它应该是截至以后元素,之前所有的数组元素被回调函数解决累计的后果。
- current:以后被执行的数组元素。
- currentIndex: 以后被执行的数组元素索引。
- sourceArray:原数组,也就是调用 reduce 办法的数组。
如果不传入初始值,reduce 办法会从索引 1 开始执行回调函数,如果传入初始值,将从索引 0 开始、并从初始值的根底上累计执行回调。
计算对象数组某一属性的总和
const list = [{ name: 'left', width: 20},
{name: 'center', width: 70},
{name: 'right', width: 10},
];
const total = list.reduce((currentTotal, item) => {return currentTotal + item.width;}, 0);
// total: 100
对象数组的去重,并统计每一项反复次数
const list = [{ name: 'left', width: 20},
{name: 'right', width: 10},
{name: 'center', width: 70},
{name: 'right', width: 10},
{name: 'left', width: 20},
{name: 'right', width: 10},
];
const repeatTime = {};
const result = list.reduce((array, item) => {if (repeatTime[item.name]) {repeatTime[item.name]++;
return array;
}
repeatTime[item.name] = 1;
return [...array, item];
}, []);
// repeatTime: {left: 2, right: 3, center: 1}
// result: [// { name: 'left', width: 20},
// {name: 'right', width: 10},
// {name: 'center', width: 70},
// ]
对象数组最大 / 最小值获取
const list = [{ name: 'left', width: 20},
{name: 'right', width: 30},
{name: 'center', width: 70},
{name: 'top', width: 40},
{name: 'bottom', width: 20},
];
const max = list.reduce((curItem, item) => {return curItem.width >= item.width ? curItem : item;});
const min = list.reduce((curItem, item) => {return curItem.width <= item.width ? curItem : item;});
// max: {name: "center", width: 70}
// min: {name: "left", width: 20}
reduce 很弱小,更多奇技淫巧举荐查看这篇《25 个你不得不晓得的数组 reduce 高级用法》
性能比照
说了这么多,那这些遍历办法,在性能上有什么差别呢?咱们在 Chrome 浏览器中尝试。我采纳每个循环执行 10 次,去除最大、最小值 取平均数,升高误差。
var list = Array(100000).fill(1)
console.time('for');
for (let index = 0, len = list.length; index < len; index++) {
}
console.timeEnd('for');
// for: 2.427642822265625 ms
console.time('every');
list.every(() => { return true})
console.timeEnd('every')
// some: 2.751708984375 ms
console.time('some');
list.some(() => { return false})
console.timeEnd('some')
// some: 2.786590576171875 ms
console.time('foreach');
list.forEach(() => {})
console.timeEnd('foreach');
// foreach: 3.126708984375 ms
console.time('map');
list.map(() => {})
console.timeEnd('map');
// map: 3.743743896484375 ms
console.time('forof');
for (let index of list) {
}
console.timeEnd('forof')
// forof: 6.33380126953125 ms
从打印后果能够看出,for 循环的速度最快,for of 循环最慢
罕用遍历的终止、性能表格比照
是否可终止 | ||||
---|---|---|---|---|
** | break | continue | return | 性能(ms) |
for | 终止 ✅ | 跳出本次循环 ✅ | ❌ | 2.42 |
forEach | ❌ | ❌ | ❌ | 3.12 |
map | ❌ | ❌ | ❌ | 3.74 |
for of | 终止 ✅ | 跳出本次循环 ✅ | ❌ | 6.33 |
some | ❌ | ❌ | return true ✅ | 2.78 |
every | ❌ | ❌ | return false ✅ | 2.75 |
最初,不同浏览器内核 也会有些差别, 有趣味的同学也能够尝试一下。
对象遍历
在对象遍历中,常常须要遍历对象的键、值,ES5 提供了 for…in 用来遍历对象,然而其波及对象属性的“可枚举属性”、原型链属性等,上面将从 Object 对象实质探寻各种遍历对象的办法,并辨别罕用办法的一些特点。
for in
Object.prototype.fun = () => {};const obj = {2: 'a', 1: 'b'};for (const i in obj) {console.log(i, ':', obj[i]);}// 1: b// 2: a// fun : () => {} Object 原型链上扩大的办法也被遍历进去 for (const i in obj) {if (Object.prototype.hasOwnProperty.call(obj, i)) {console.log(i, ':', obj[i]); }}// name : a 不属于本身的属性将被 hasOwnProperty 过滤
小结
应用 for in 循环时,返回的是所有可能通过对象拜访的、可枚举的属性,既包含存在于实例中的属性,也包含存在于原型中的实例。如果只须要获取对象的实例属性,能够应用 hasOwnProperty 进行过滤。
应用时,要应用 (const x in a)
而不是 (x in a)
后者将会创立一个全局变量。
for in 的循环程序,参考【JavaScript 权威指南】(第七版)6.6.1。
- 先列出名字为非负整数的字符串属性,依照数值程序从最小到最大。这条规定意味着数组和类数组对象的属性会依照程序被枚举。
- 在列出类数组索引的所有属性之后,在列出所有剩下的字符串名字(包含看起来像整正数或浮点数的名字)的属性。这些属性依照它们增加到对象的先后顺序列出。对于在对象字面量中定义的属性,依照他们在字面量中呈现的程序列出。
- 最初,名字为符号对象的属性依照它们增加到对象的先后顺序列出。
Object.keys
Object.prototype.fun = () => {};const str = 'ab';console.log(Object.keys(str));// ['0', '1']const arr = ['a', 'b'];console.log(Object.keys(arr));// ['0', '1']const obj = {1: 'b', 0: 'a'};console.log(Object.keys(obj));// ['0', '1']
小结
用于获取对象本身所有的可枚举的属性值,但不包含原型中的属性,而后返回一个由属性名组成的数组。
Object.values
Object.prototype.fun = () => {};const str = 'ab';console.log(Object.values(str));// ['a', 'b']const arr = ['a', 'b'];console.log(Object.values(arr));// ['a', 'b']const obj = {1: 'b', 0: 'a'};console.log(Object.values(obj));// ['a', 'b']
小结
用于获取对象本身所有的可枚举的属性值,但不包含原型中的属性,而后返回一个由属性值组成的数组。
Object.entries
const str = 'ab';for (const [key, value] of Object.entries(str)) {console.log(`${key}: ${value}`);}// 0: a// 1: bconst arr = ['a', 'b'];for (const [key, value] of Object.entries(arr)) {console.log(`${key}: ${value}`);}// 0: a// 1: bconst obj = {1: 'b', 0: 'a'};for (const [key, value] of Object.entries(obj)) {console.log(`${key}: ${value}`);}// 0: a// 1: b
小结
用于获取对象本身所有的可枚举的属性值,但不包含原型中的属性,而后返回二维数组。每一个子数组由对象的属性名、属性值组成。能够同时拿到属性名与属性值的办法。
Object.getOwnPropertyNames
Object.prototype.fun = () => {};Array.prototype.fun = () => {};const str = 'ab';console.log(Object.getOwnPropertyNames(str));// ['0', '1', 'length']const arr = ['a', 'b'];console.log(Object.getOwnPropertyNames(arr));// ['0', '1', 'length']const obj = {1: 'b', 0: 'a'};console.log(Object.getOwnPropertyNames(obj));// ['0', '1']
小结
用于获取对象本身所有的可枚举的属性值,但不包含原型中的属性,而后返回一个由属性名组成的数组。
总结
咱们比照了多种罕用遍历的办法的差别,在理解了这些之后,咱们在应用的时候须要好好思考一下,就能晓得那个办法是最合适的。欢送大家纠正补充。
举荐浏览
聊聊 Deno 的那些事
H5 页面列表缓存计划
开源作品
- 政采云前端小报
开源地址 www.zoo.team/openweekly/ (小报官网首页有微信交换群)
招贤纳士
政采云前端团队(ZooTeam),一个年老富裕激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 40 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员形成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端利用、数据分析及可视化等方向进行技术摸索和实战,推动并落地了一系列的外部技术产品,继续摸索前端技术体系的新边界。
如果你想扭转始终被事折腾,心愿开始能折腾事;如果你想扭转始终被告诫须要多些想法,却无从破局;如果你想扭转你有能力去做成那个后果,却不须要你;如果你想扭转你想做成的事须要一个团队去撑持,但没你带人的地位;如果你想扭转既定的节奏,将会是“5 年工作工夫 3 年工作教训”;如果你想扭转原本悟性不错,但总是有那一层窗户纸的含糊… 如果你置信置信的力量,置信平凡人能成就不凡事,置信能遇到更好的本人。如果你心愿参加到随着业务腾飞的过程,亲手推动一个有着深刻的业务了解、欠缺的技术体系、技术发明价值、影响力外溢的前端团队的成长历程,我感觉咱们该聊聊。任何工夫,等着你写点什么,发给 ZooTeam@cai-inc.com