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() 不包含 ❌ 包含 ✅ 包含 ✅
相关推荐
TYC911 分钟前
Mac创建Vue3+TypeScript或者React+TypeScript项目
前端·javascript·typescript
救救孩子把9 小时前
AI面试八股文-21_21-多模态Agent面试题-Vision-Language-Agent
人工智能·面试·职场和发展·llava·多模态agent
持敬chijing10 小时前
白盒测试与代码审计-js登录逆向实战
javascript·网络·安全·web安全·网络安全·网络攻击模型
优选资源分享11 小时前
PicGo v3.0.3 新版 | 开源免费图床工具
javascript·笔记·electron
ShineWinsu12 小时前
对于JavaScript:JavaScript基础语法的解析
开发语言·前端·javascript·面试·web·基础语法·循环
救救孩子把12 小时前
AI面试八股文-13_12-AI 框架与运维面试题
运维·人工智能·面试
普通人612 小时前
php+html+canvas实现树状打开的结构
开发语言·javascript·html5·1024程序员节
救救孩子把13 小时前
AI面试八股文-14_14-MCP-Skill-Function Calling
人工智能·安全·面试·职场和发展·推理·mcp·functioncall
猫头_13 小时前
AI 流式传输工程指南:模型吐出半截 JSON,前端如何边收边渲染?
前端·javascript·llm
我的div丢了肿么办15 小时前
element-plus表格使用:filter-method过滤导致出现无数据
前端·javascript·vue.js