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() 不包含 ❌ 包含 ✅ 包含 ✅
相关推荐
mONESY1 小时前
React 前端如何不傻等后端接口?
前端·javascript·后端
xiaominlaopodaren1 小时前
three.js地图数学基础(七):地图相机
javascript·gis·three.js
淡海水1 小时前
C#与常用数据结构源码剖析-全篇导览
开发语言·数据结构·unity·面试·职场和发展·c#
vx-程序开发1 小时前
django汽车租赁系统---附源码25360
java·javascript·spring boot·python·eclipse·django·php
满栀5852 小时前
vue3动态路由详细效果
前端·javascript·vue.js·typescript·前端框架
山荷枝2 小时前
05-Vue
前端·javascript·vue.js
breeze jiang3 小时前
Next.js App Router 全栈实战:从 SPA 的 SEO 痛点到服务端组件与 Hydration 水合机制
开发语言·javascript·ecmascript
可爱的秋秋啊3 小时前
vue调用腾讯人脸组件封装+接口请求后端调用
前端·javascript·vue.js
绝世唐门三哥3 小时前
CSS 虚线下划线用法指南:text-decoration 完整解析
前端·javascript·css