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 底层认定为整数索引:

  • 类型匹配:它是一个非负整数的字符串形式(或者能被转换为该形式的数字)。
  • 范围限制:它的数值必须落在 0 到 2^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()

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

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() 不包含 ❌ 包含 ✅ 包含 ✅
相关推荐
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
晨米酱3 天前
AGENTS.md:Agent 的上下文策略层
面试·架构·agent
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄3 天前
JavaScript 隐式全局变量解析
javascript
彧azz3 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
沙蒿同学3 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
CoderYanger3 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
paopaokaka_luck3 天前
智慧社区综合服务小程序(人脸识别、AI问答、Echarts图形化分析)
前端·javascript·spring boot·spring·数据分析·echarts
爱学习的执念3 天前
助力金九银十1000道软件测试面试题(功能、接口、自动化、WEB、APP.......)附答案
软件测试·面试
小的~~3 天前
银河麒麟V10 ARM部署TDengine(涛思)工业时序数据库安装与测试
面试·程序员创富