文章目录▼CloseOpen
- 为什么你遍历对象时总踩坑?先搞懂这3个核心问题
- 前端工程师都在用的3种灵活遍历方法,附实战示例
- 遍历对象时如何避免拿到原型链上的属性?
- Object.entries遍历的键值对顺序是固定的吗?
- Object.entries不支持IE,有没有替代方案?
- 只需要对象的值,用Object.values还是Object.entries?
- for…in和Object.keys的核心区别是什么?
为什么你遍历对象时总踩坑?先搞懂这3个核心问题
很多人遍历对象出错,不是因为方法不会用,是没搞清楚这三个问题,我一个个给你掰碎了说:
第一个问题:你要遍历的是“对象自身的属性”还是“包括原型链的属性”? 比如Object.prototype
是所有对象的原型,要是有人给它加了个自定义属性(比如Object.prototype.myProp = 'test'
),用for...in
遍历任何对象都会拿到myProp
,这就是我朋友之前踩的坑。而Object.entries
、Object.keys
这些方法不一样——它们只遍历对象自身的可枚举属性,完全不会碰原型链,所以更安全。我现在写代码,除非特殊情况,基本不用for...in
,就是怕踩这个坑。
第二个问题:你在意键值对的顺序吗? ES6之后,对象的属性顺序有了明确规则:数字键(比如1、2、3)会按升序排列,字符串键(比如’name’、’age’)会按插入顺序排列,Symbol键会按插入顺序排在最后。比如const obj = {10: 'a', 2: 'b', 'c': 'c'}; Object.entries(obj)
会返回[[ '2', 'b' ], [ '5', 'c' ], [ '10', 'a' ]]
——数字键先按升序排,再排字符串键。我之前做月份统计的时候,用数字键1-12存月份名称,遍历出来正好是1月到12月,没问题;但如果是用字符串键’10’、’2’,遍历出来就是’2’在前、’10’在后,这点要注意,要是顺序重要,最好统一键的类型。
第三个问题:你是要“单独处理键”“单独处理值”,还是“键值对一起处理”? 比如你要检查对象里的必填字段有没有空值,只需要键就行(用Object.keys
遍历,然后判断每个键对应的值);要是你要把对象的值转成数组给图表组件,只需要值(用Object.values
);要是你要把键值对转成标签列表,就得一起处理(用Object.entries
)。我之前做用户信息卡片的时候,一开始用Object.keys
遍历键,再一个个取value,写了三行代码:
const user = {name: '张三', age: 25, job: '前端'};
const labels = Object.keys(user).map(key => {
return ${key}: ${user[key]}
;
});
后来同事看了说:“你直接用Object.entries
多好啊!”改成这样:
const labels = Object.entries(user).map(([key, value]) => {
return ${key}: ${value}
;
});
代码少了一行,还不用再写user[key]
,当时我就觉得“原来还有这招?”
前端工程师都在用的3种灵活遍历方法,附实战示例
我把自己日常用得最多的3种方法列出来,每个方法都附实战场景,你直接套就行。
Object.entries
应该是我用得最多的遍历方法了——它直接返回一个[[key, value]]
的数组,不管是forEach
还是map
,处理起来都超方便。比如我做电商项目的购物车组件时,要遍历商品的属性对象(比如{color: '红色', size: 'M', stock: '有货'}
),把这些属性展示成标签,用Object.entries
一行代码就搞定:
const props = {color: '红色', size: 'M', stock: '有货'};
const propTags = Object.entries(props).map(([key, value]) => {
return ${key}: ${value}
;
});
这样生成的propTags
数组直接就能渲染到页面上,比用Object.keys
再取value省了一步。
还有一次做订单详情页,要把订单的扩展信息(比如{logistics: '顺丰', deliveryTime: '次日达', discount: '满减20元'}
)转成表格行,用Object.entries
遍历,然后生成
const extInfo = {logistics: '顺丰', deliveryTime: '次日达', discount: '满减20元'};
const tableRows = Object.entries(extInfo).map(([key, value]) => {
return ${key} ${value}
;
});
是不是超灵活?
这里要注意兼容性——Object.entries
是ES6的方法,现代浏览器(Chrome、Firefox、Edge)都支持,但IE11及以下不支持。要是你要兼容IE,可以用Babel转译,或者加Polyfill(比如@babel/polyfill
),我之前做企业官网项目的时候,就用了@babel/polyfill
,完美解决兼容问题。
要是你只需要键或者只需要值,用Object.keys
(键数组)或Object.values
(值数组)更高效。比如我做表单验证的时候,要检查对象里的必填字段有没有空值,用Object.keys
遍历键,然后判断每个键对应的值:
const formData = {username: 'zhangsan', password: '', email: 'zhangsan@test.com'};
const isRequiredFilled = Object.keys(formData).every(key => {
// 假设username、password是必填项
return ['username', 'password'].includes(key) ? formData[key] !== '' true;
});
这样就能快速判断必填项有没有空值。
再比如我做数据可视化的时候,要把对象的值转成数组给ECharts组件,用Object.values
直接转:
const salesData = {Jan: 1000, Feb: 1500, Mar: 1200};
// ECharts需要的值数组是[1000, 1500, 1200]
const chartData = Object.values(salesData);
比用for循环一个个push进去方便多了。
我之前还做过一个评分组件,要把评分对象({accuracy: 4.5, speed: 4.2, service: 4.8}
)的平均值算出来,用Object.values
加reduce
:
const scores = {accuracy: 4.5, speed: 4.2, service: 4.8};
const average = Object.values(scores).reduce((sum, score) => sum + score, 0) / Object.values(scores).length;
一行代码算平均值,是不是很简洁?
要是你维护的是老项目(比如用jQuery 1.x的项目),或者要兼容IE10及以下浏览器,Object.entries
这些方法可能不支持,这时候就用for...in
,但一定要加hasOwnProperty
判断,不然会踩原型链的坑。比如我之前维护一个老后台系统,用的是jQuery 1.8,要遍历配置对象:
const config = {apiUrl: 'https://api.test.com', timeout: 5000, retry: 3};
for (let key in config) {
// 只处理对象自身的属性
if (config.hasOwnProperty(key)) {
console.log(键:${key},值:${config[key]}
);
}
}
加了hasOwnProperty
之后,就算原型链上有额外属性,也不会被遍历到,安全多了。
我之前还碰到过一个更老的项目,用的是IE9,连Object.keys
都不支持,只能用for...in
加hasOwnProperty
,虽然代码比现代方法长一点,但胜在稳妥。
为了让你更清楚这三种方法的区别,我做了个表格,你一看就懂:
方法 | 核心特点 | 最佳场景 | 兼容性 |
---|---|---|---|
Object.entries | 返回[key, value]数组,遍历自身属性 | 需要同时处理键和值 | 现代浏览器(Chrome 54+、Firefox 47+),IE不支持 |
Object.keys/values | 返回键或值的数组,遍历自身属性 | 只需要键或只需要值 | 现代浏览器,IE不支持 |
for...in + hasOwnProperty | 遍历所有可枚举属性,过滤原型链 | 兼容旧环境(IE10及以下) | 所有浏览器都支持 |
最后再给你提个醒:遍历对象时,尽量避免用for
循环逐个遍历——比如let keys = Object.keys(obj); for (let i = 0; i < keys.length; i++) { ... }
,这种方法比Object.entries
或forEach
麻烦多了,而且可读性差。我之前刚学前端的时候,就爱用这种方法,后来看了同事的代码才知道,原来有更简洁的方法。
这些方法我用了三年,从电商项目到后台管理系统,基本覆盖了所有遍历场景。你可以试试把你项目里的旧遍历代码换成这些方法,比如把for...in
加hasOwnProperty
换成Object.entries
,看看代码是不是更简洁了?如果试了之后有效果,或者碰到了新问题,欢迎回来告诉我!
我平时做数据统计的时候,经常碰到只需要对象里“值”的情况——比如上个月做月度销量统计,后台返回的是{1月: 1200, 2月: 1500, 3月: 1300}
这样的对象,我要算总销量,直接用Object.values
把值转成数组[1200, 1500, 1300]
,再用reduce
加起来就行,一步到位。要是换Object.entries
呢?得先拿到[[1月,1200],[2月,1500],[3月,1300]]
,再用map
把每个数组的第二个元素挑出来,多写一行代码不说,别人看的时候还得反应一下:“你明明只要值,为啥要带键一起拿?”
还有一次帮同事调表单代码,他做了个“用户兴趣”的多选组件,选了的兴趣存在对象里是{音乐: true, 运动: false, 阅读: true}
,他要统计有多少个兴趣被选中,居然用了Object.entries
遍历,写了entries.map(([key, value]) => value).filter(Boolean).length
。我跟他说:“你直接用Object.values
多省事儿啊!”改成Object.values(interestObj).filter(Boolean).length
,代码少了一截,可读性还高——别人一眼就懂“哦,他在拿所有值里的true”。其实不是Object.entries
不好,而是当你明确只需要“值”的时候,没必要把“键”也一起拽过来,就像你去便利店买瓶水,没必要连货架一起搬回家对吧?
再比如给ECharts组件传数据,series.data
需要一个纯值数组,要是你用Object.entries
,还得额外转一层map
取value,反而画蛇添足。我现在写代码有个习惯:先想“我要的是键、值,还是键值对?”如果只需要值,第一反应就是Object.values
——毕竟简洁才是代码的第一生产力,能少写一行是一行。
遍历对象时如何避免拿到原型链上的属性?
可以优先使用Object.entries、Object.keys或Object.values方法,这些方法只会遍历对象自身的可枚举属性,不会包含原型链上的内容;若需兼容IE10及以下旧环境,可用for...in循环搭配hasOwnProperty判断,手动过滤原型链属性。
Object.entries遍历的键值对顺序是固定的吗?
是的,ES6后对象属性顺序有明确规则:数字键(如1、2、3)按升序排列,字符串键(如'name'、'age')按插入顺序排列,Symbol键按插入顺序排在最后。例如对象{10: 'a', 2: 'b', 'c': 'c'},Object.entries返回的顺序是[[ '2', 'b' ], [ '10', 'a' ], [ 'c', 'c' ]]。
Object.entries不支持IE,有没有替代方案?
有两种方案:一是用for...in循环加hasOwnProperty判断,这是所有浏览器都支持的传统方法;二是引入Babel Polyfill(如@babel/polyfill),让IE11及以下版本支持Object.entries等现代方法。
只需要对象的值,用Object.values还是Object.entries?
优先选Object.values,它直接返回对象自身值的数组,无需额外处理键,代码更简洁;Object.entries返回的是[key, value]数组,若只需要值,会多一步提取操作,效率略低。
for...in和Object.keys的核心区别是什么?
for...in会遍历对象自身及原型链上的可枚举属性,需手动过滤;Object.keys只遍历对象自身的可枚举属性,不会碰原型链。 除非需要兼容旧环境,更推荐用Object.keys或Object.entries。