JS对象的遍历

准备工作

先定义好一个对象 obj,后续都用它来遍历并查看结果。

  • 键的类型包括 String(含数字字符串)、Symbol
  • 包含不可枚举属性
  • 包含原型
typescript 复制代码
const proto = {
  [Symbol('bb')]: Symbol('bb'), [Symbol('aa')]: Symbol('aa'),
  2.2: 2.2, 20: 20, 1.6: 1.6, 5: 5, 0.6: 0.6,
  cc: 'cc', bb: 'bb', false: false,
};
Object.defineProperty(proto, '3', {
  value: 3,
  enumerable: false,
});
Object.defineProperty(proto, 'aa', {
  value: 'aa',
  enumerable: false,
});

const obj = {
  [Symbol('b')]: Symbol('b'), [Symbol('a')]: Symbol('a'),
  2.1: 2.1, 10: 10, 5: 5, 0.9: 0.9, 0: 0, '-5': -5, 0.3: 0.3,
  c: 'c', b: 'b', true: true,
};
Object.defineProperty(obj, '2', {
  value: 2,
  enumerable: false,
});
Object.defineProperty(obj, 'a', {
  value: 'a',
  enumerable: false,
});

Object.setPrototypeOf(obj, proto);

遍历顺序

ES6 开始,引擎底层定义了一套标准遍历顺序规则。绝大多数现代 API 都严格遵循这个顺序:

  1. 先遍历整数索引键,按数值从小到大升序遍历
  2. 再遍历 String 键,按插入顺序遍历
  3. 最后遍历 Symbol 键,按插入顺序遍历

根据 ECMAScript 规范定义,一个属性键如果满足以下两个条件,就会被 JS 底层认定为整数索引

  • 类型匹配:它是一个非负整数的字符串形式(或者能被转换为该形式的数字)。
  • 范围限制:它的数值必须落在 02^32 - 2 之间,即 [0, 4,294,967,294]

所以,除此之外的包括浮点数、负数等都会被当做 String 键,按插入顺序遍历。( :即使是 3.0 这种一样是浮点数,同样被当做 String 键,按插入顺序遍历)

不可枚举属性同样适用上述遍历顺序规则。

当遍历范围包括原型属性时,先遍历自有属性,再遍历原性属性(若原型对象上面还有原型则继续分层一直到顶),各自内部的遍历顺序同上。 注意:若不同层级遇到同名属性时,低层级覆盖高层级。

遍历方式

以下方法可以直接遍历对象:

for...in

迭代目标对象所有自有和继承的 String 属性键,仅包括可枚举属性。

typescript 复制代码
const result = [];

for (const key in obj) {
  result.push([key, obj[key]]);
}

console.log(result);
// [['0',0],['5',5],['10',10],['2.1',2.1],['0.9',0.9],['-5',-5],['0.3',0.3],['c','c'],['b','b'],['true',true],
// ['20',20],['2.2',2.2],['1.6',1.6],['0.6',0.6],['cc','cc'],['bb','bb'],['false',false]]

Object.keys()、Object.values()、Object.entries()

Object.keys() 返回一个由目标对象自有 String 属性键 组成的数组,仅包括可枚举属性。 Object.values() 返回一个由目标对象自有 String 属性值 组成的数组,仅包括可枚举属性。 Object.entries() 返回一个由目标对象自有 String 属性键值组成的二维数组,仅包括可枚举属性。

typescript 复制代码
Object.keys(obj);
// ['0', '5', '10', '2.1', '0.9', '-5', '0.3', 'c', 'b', 'true']

Object.values(obj);
// [0, 5, 10, 2.1, 0.9, -5, 0.3, 'c', 'b', true]

Object.entries(obj);
// [['0', 0],['5', 5],['10', 10],['2.1', 2.1],['0.9', 0.9],['-5', -5],['0.3', 0.3],['c', 'c'],['b', 'b'],['true', true]]

Object.getOwnPropertyNames()

返回一个由目标对象自有 String 属性键组成的数组,包括不可枚举属性。

typescript 复制代码
Object.getOwnPropertyNames(obj);
// ['0', '2', '5', '10', '2.1', '0.9', '-5', '0.3', 'c', 'b', 'true', 'a']

Object.getOwnPropertySymbols()

返回一个由目标对象自有 Symbol 属性键组成的数组,包括不可枚举属性。

typescript 复制代码
Object.getOwnPropertySymbols(obj);
// [Symbol(b), Symbol(a)]

Reflect.ownKeys()

返回一个由目标对象自有 StringSymbol 属性键组成的数组,包括不可枚举属性。

typescript 复制代码
Reflect.ownKeys(obj);
// ['0', '2', '5', '10', '2.1', '0.9', '-5', '0.3', 'c', 'b', 'true', 'a', Symbol(b), Symbol(a)]

Reflect.ownKeys() 等同于 Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))

方式对比

方法 是否包含原型链属性 是否包含不可枚举属性 是否包含Symbol类型键
for...in 包含 ✅ 不包含 ❌ 不包含 ❌
Object.keys() Object.values() Object.entries() 不包含 ❌ 不包含 ❌ 不包含 ❌
Object.getOwnPropertyNames() 不包含 ❌ 包含 ✅ 不包含 ❌
Object.getOwnPropertySymbols() 不包含 ❌ 包含 ✅ 包含 ✅
Reflect.ownKeys() 不包含 ❌ 包含 ✅ 包含 ✅
相关推荐
小磊哥er16 分钟前
深入解构Claude Code - 第 12 篇 · 整体串起来
javascript·ai编程
小磊哥er18 分钟前
深入解构Claude Code - 第 11 篇 · 工程上的讲究
javascript·ai编程
独立开发之道3 小时前
【three.js教程】Three.js 运行时更新:矩阵、几何体、材质怎么改才不卡
javascript·矩阵·材质
Phil3233 小时前
开源一个 Windows 实时面试助手:听译、置顶提词、按需生成英文稿
面试·github
默_笙4 小时前
🌃 HTTP 不认识你:JWT 登录鉴权的完整"酒店入住"指南
前端·javascript
奋斗吧程序媛4 小时前
CSV文件导入功能
开发语言·javascript·ecmascript
梨想橙汁5 小时前
JS 核心语法:运算符流程控制、函数、数组与对象实战详解
前端·javascript
七牛开发者5 小时前
拆解 dsh:Turn 与 Step 如何组织 Agent 主循环
前端·javascript·人工智能
空想兔5 小时前
前端网络请求的前世今生:从 Ajax、XHR 到 Fetch 和 Axios
javascript
梨想橙汁5 小时前
JavaScript 零基础入门:引入方式、变量与数据类型,吃透原始与引用类型
前端·javascript