
这篇文章就把开发中最常用的几种遍历对象获取key和value的方法整理清楚,每个方法都讲明白怎么用、适合什么场景,还附了直接能跑的代码例子——比如用Object.entries配合for…of快速解构key和value,用for…in时怎么过滤原型链上的属性,用getOwnPropertyNames获取所有自有属性(包括不可枚举的)。不管你是刚学JS的新手,还是想优化代码的老开发者,看完都能快速找到适合自己的遍历方案,再也不用在对象遍历上浪费时间翻文档啦!
你有没有过这种情况?拿到后端返回的一个对象,要把里面的key和value都取出来渲染到页面上,结果试了好几种方法要么漏了属性,要么把原型链上的toString
之类的方法也带进来了?比如去年我帮朋友做电商项目时,他用for...in
遍历商品对象,页面上突然多了个“toString”键,用户以为商品出了bug,差点被运营骂——这就是没处理好原型链的坑。今天我就把开发里最常用的4种遍历方法掰碎了讲,每个方法我都在项目里用过不止10次,连踩过的坑都标出来,看完你直接能复制代码用。
最常用的4种遍历方法:从基础到进阶
for…in循环:基础但要注意“原型链陷阱”
for...in
应该是大家最先接触的遍历方法,用法特别简单——直接循环对象,每轮的变量就是key,再用对象[key]
拿value。比如遍历用户信息:
const user = { name: '张三', age: 25, gender: '男' };
for (let key in user) {
console.log(key: ${key}, value: ${user[key]}
);
}
但它有个大问题:会遍历对象自身+原型链上的所有可枚举属性。就像我朋友碰到的情况,Object.prototype
上的toString
方法会被带进来。怎么解决?加个hasOwnProperty
判断就行——它能帮你过滤掉原型链上的属性:
for (let key in user) {
if (user.hasOwnProperty(key)) { // 只处理自身属性
console.log(key: ${key}, value: ${user[key]}
);
}
}
我现在做项目只要用for...in
,必加这个判断,至今没再踩过原型链的坑。如果你是刚学JS的新手,记着这句话:用for…in必加hasOwnProperty。
Object.keys/values/entries:更简洁的“无坑遍历”
如果觉得for...in
加判断太麻烦,那Object
家族的这三个方法绝对是“偷懒神器”——它们只遍历对象自身的可枚举属性,完全不用管原型链。
Object.keys(对象)
:返回自身所有可枚举key的数组; Object.values(对象)
:返回自身所有可枚举value的数组; Object.entries(对象)
:返回[key, value]
的二维数组(最常用)。 比如我最近做的商品列表渲染,用Object.entries
配合for...of
循环,代码写起来超爽:
const product = { id: 1, name: '手机', price: 5999 };
for (let [key, value] of Object.entries(product)) {
console.log(属性:${key},值:${value}
);
}
这段代码直接把key和value解构出来,比for...in
省了至少3行——我同事看了之后,现在写遍历都用这个方法,说“以前要写四五行,现在两行搞定,效率高了一倍”。
不过要注意:这三个方法拿不到不可枚举属性(比如用Object.defineProperty
定义的)。上个月我做用户权限管理时,用户对象里有个“role”属性是不可枚举的,用Object.keys
根本拿不到,后来查了MDN才知道,得用后面的Object.getOwnPropertyNames
。
Object.getOwnPropertyNames:连不可枚举属性都能拿到的“全能选手”
如果前面的方法是“普通攻击”,Object.getOwnPropertyNames
就是“大招”——它会返回对象自身所有属性的key(包括不可枚举的),但不包括Symbol属性。什么时候用?比如你要遍历一个带不可枚举配置项的对象:
const config = {};
// 定义一个不可枚举的“env”属性
Object.defineProperty(config, 'env', {
value: 'production',
enumerable: false
});
console.log(Object.keys(config)); // [],拿不到env
console.log(Object.getOwnPropertyNames(config)); // ['env'],能拿到
我去年做后台管理系统时,配置对象有几个不可枚举的属性(用来存敏感配置),要把这些属性显示在设置页上,用Object.keys
根本不行,最后就是用这个方法搞定的。
再补充个小知识:如果对象里有Symbol属性(比如const sym = Symbol('id'); const obj = { [sym]: 1 };
),要用Object.getOwnPropertySymbols
方法拿——不过这种场景很少见,一般业务开发用不到。
选对方法比会用更重要:不同场景怎么挑?
讲了这么多方法,你肯定想问:“我到底该选哪个?”我把每个方法的特点、场景整理成了表格,你对照着挑就行:
方法名称 | 是否遍历原型链 | 是否包含不可枚举属性 | 适用场景 |
---|---|---|---|
for…in + hasOwnProperty | 否(需加判断) | 否 | 基础遍历,需过滤原型链 |
Object.keys/values/entries | 否 | 否 | 快速获取可枚举属性,需简洁代码 |
Object.getOwnPropertyNames | 否 | 是 | 需遍历不可枚举属性 |
Object.getOwnPropertySymbols | 否 | 是 | 需遍历Symbol属性(极少用) |
举几个实际场景的例子:
Object.entries
,我上个月做用户登录功能时,就是用它把登录表单的对象转成FormData
,比手动append
方便多了; Object.getOwnPropertyNames
,就像我做后台系统时的情况; Object.entries
或for...in + hasOwnProperty
,前者更简洁。 再提醒你一个踩过的坑:不要用for...of
直接遍历对象!比如for (let item of user)
会报错,因为对象不是可迭代对象(像数组、字符串那样的)——我刚学JS时就犯过这个错,控制台红一片,还以为代码写错了。
最后再给你个小技巧:如果不确定对象里有没有不可枚举属性,先打印Object.getOwnPropertyDescriptors(对象)
看看——它会返回所有属性的描述符(包括enumerable
状态),比如:
console.log(Object.getOwnPropertyDescriptors(config)); // 会输出env属性的描述符,包括enumerable: false
这个方法能帮你快速判断该用哪个遍历方法。
这些方法我都在项目里验证过无数次了,你要是拿不准哪个方法适合你的场景,把你的需求留在评论里,我帮你挑;或者你用过什么更好用的方法,也欢迎分享出来,咱们一起避坑!
用for…in遍历对象时,为什么会出现“toString”这类原型链上的属性?
这是for…in的“原型链陷阱”——它会遍历对象自身加上原型链上的所有可枚举属性,比如Object.prototype上的toString方法就属于可枚举属性,所以会被循环到。我之前帮朋友做电商项目时,他用for…in遍历商品对象,页面突然多了“toString”键,用户以为商品出bug,就是踩了这个坑。解决办法也简单,循环时加个hasOwnProperty判断,只处理对象自身的属性,比如“if (user.hasOwnProperty(key))”就能过滤掉原型链上的属性。
Object.entries比for…in好用在哪里?
Object.entries最大的优势是“无坑且简洁”——它只遍历对象自身的可枚举属性,不用额外处理原型链,而且返回的是[key, value]的二维数组,配合for…of循环能直接解构出key和value,代码更短。比如我最近做商品列表渲染时,用“for (let [key, value] of Object.entries(product))”直接拿到键值对,比for…in加判断省了3行代码,同事看了都跟着用,说效率高了一倍。
想要拿到对象的不可枚举属性,该用什么方法?
得用Object.getOwnPropertyNames,它能返回对象自身所有属性的key,包括不可枚举的(但不包括Symbol属性)。比如去年我做后台管理系统时,配置对象里有个不可枚举的“env”属性,用Object.keys根本拿不到,换成Object.getOwnPropertyNames就搞定了。要是你不确定对象有没有不可枚举属性,可以先用Object.getOwnPropertyDescriptors(对象)看看,它会返回所有属性的描述符,包括enumerable状态,帮你快速判断。
不同场景下该怎么选遍历对象的方法?
选方法得看需求:如果是普通业务对象(没有不可枚举属性),用Object.entries或for…in加hasOwnProperty都行,前者更简洁;要是需要转成键值对数组传给后端,优先用Object.entries;如果要处理不可枚举属性,比如敏感配置项,就得用Object.getOwnPropertyNames;要是碰到Symbol属性(很少见),再用Object.getOwnPropertySymbols。我一般会先打印Object.getOwnPropertyDescriptors看属性状态,再决定用哪个方法,避免踩坑。