☰
JavaScript 计算对象属性数量:Object.keys().length 一行代码实战与边界详解
2026/10/7 2:30:35 网站建设 项目流程
  • 文档
  • 教程
  • 前端

【免费下载链接】zh.javascript.info

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

项目地址:https://gitcode.com/gh_mirrors/zh/zh.javascript.info
点击查看免费下载

本练习来自现代 JavaScript 教程(zh.javascript.info)"Object.keys,values,entries"章节的配套任务02-count-properties。教程要求编写一个count(obj)函数返回对象属性数量,并"尽量使代码简短"。本文以该任务为核心,从 task.md 的任务定义出发,结合仓库中真实存在的 solution.js 与 test.js,讲解一行代码的解答思路、Object.keys的底层行为、Symbol 键与空对象的边界情况,以及如何在测试用例中验证这些行为。读完本文,你将掌握统计对象"常规"属性数量的标准写法,并理解它背后的遍历约定与取舍。

任务定义:写一个最简的count(obj)

原始任务(见 task.md)非常明确:

写一个函数count(obj),该函数返回对象中的属性的数量,并试着使代码尽可能简短。

let user = { name: 'John', age: 30 }; alert( count(user) ); // 2

任务还有一个重要附注(P.S.):

忽略 Symbol 类型属性,只计算"常规"属性。

这实际上是对"属性"一词的精确定义:这里的"属性"指以字符串为键的可枚举属性,而不是对象上所有可枚举的键。这个限定直接决定了应该选用哪个 API,也是本练习考察的重点之一。

标准解答:一行代码Object.keys(obj).length

仓库中该任务的官方解答(solution.js)只有短短三行:

function count(obj) { return Object.keys(obj).length; }

其核心就一句话:属性的数量 =Object.keys(obj)返回数组的长度。

对应到任务示例:

let user = { name: 'John', age: 30 }; Object.keys(user); // ["name", "age"] Object.keys(user).length; // 2

Object.keys(obj)返回一个包含该对象所有(可枚举)键的数组,这正是 article.md 中对Object.keys的定义。数组自带length属性,取.length即得到键的个数,也就是属性的个数,因此函数体压缩为一条return语句,满足了"代码尽可能简短"的要求。

原理拆解:为什么是Object.keys而不是obj.keys()

要真正理解这个一行解法,需要回到 article.md 中讲的两点约定:

1. 调用语法是Object.keys(obj),而不是obj.keys()

与Map、Set的map.keys()、set.keys()不同,普通对象使用静态方法Object.keys(obj)。原因在于灵活性:JavaScript 中对象是一切复杂结构的基础,我们完全可能自定义一个data对象并实现自己的data.values()方法,而此时仍然可以对它调用Object.values(data),两者互不干扰。同理,即使对象上存在自定义的同名方法,Object.keys依然可用。

2. 返回值是"真正的"数组

Map.keys()返回的是可迭代对象,而Object.keys(obj)返回的是真正的数组对象(这主要是历史原因)。正是这个区别,让我们可以直接访问其.length属性——如果返回值只是一个可迭代对象,就无法这样一行取到数量,还需要先展开成数组。

3. 遍历约定与for..in一致

Object.keys返回的键的遍历顺序与for..in循环一致。它返回的是键(key)的数组;姊妹方法Object.values(obj)返回值的数组,Object.entries(obj)返回[key, value]键值对数组:

let user = { name: "John", age: 30 }; // Object.keys(user) = ["name", "age"] // Object.values(user) = ["John", 30] // Object.entries(user)= [ ["name","John"], ["age",30] ]

关键边界一:Symbol 键被自动忽略

任务要求"忽略 Symbol 类型属性,只计算常规属性",而Object.keys恰好天生满足这个要求。

article.md 明确提示:

就像for..in循环一样,Object.keys/values/entries会忽略使用Symbol(...)作为键的属性。

这一点在仓库的测试用例 test.js 中得到了直接验证:

it("ignores symbolic properties", function() { assert.equal( count({ [Symbol('id')]: 1 }), 0 ); });

一个只含Symbol('id')键的对象,count的结果是0——Symbol 键完全没有被计入。因此,如果你的需求恰好是"只统计字符串键属性",Object.keys(obj).length就是最贴切的答案。

补充:如果确实需要把 Symbol 键也统计进来,article.md 给出了两个标准 API 作为备选:

  • Object.getOwnPropertySymbols(obj):返回只包含 Symbol 类型键的数组;
  • Reflect.ownKeys(obj):返回所有键(既包括字符串键,也包括 Symbol 键)。

它们与Object.keys的差异,正是本练习边界设计的核心知识点。

关键边界二:空对象返回 0

另一个边界是空对象。测试同样覆盖了这种情况:

it("returns 0 for an empty object", function() { assert.equal( count({}), 0 ); });

Object.keys({})返回空数组[],其.length为0,因此count({})自然返回0,无需额外编写if分支。这也是"一行代码"方案之所以简洁的原因之一——它天然处理了空对象边界。

同章节姊妹题对比:Object.values求属性总和

同一个章节目录下还有一个姊妹任务 01-sum-salaries:编写sumSalaries(salaries),用Object.values和for..of循环返回所有薪水的总和,空对象返回0。

function sumSalaries(salaries) { let sum = 0; for (let salary of Object.values(salaries)) { sum += salary; } return sum; // 650 }

对比两者可以更清晰地理解三兄弟的分工:

需求使用的 API返回关键点
统计属性个数Object.keys(obj).length数字键的数组长度
求所有属性值之和Object.values(obj)数字遍历值并累加
转换对象(如价格翻倍)Object.entries+Object.fromEntries对象键值对数组加工后转回对象

第三个场景在 article.md 的"转换对象"一节有完整示例:用Object.entries(prices).map(...)把价格翻倍,再用Object.fromEntries转回对象。可以看出,"keys/values/entries 三兄弟 + length/map/reduce 等数组方法"共同构成了处理普通对象数据的标准工具箱:先借Object.*把对象"降维"成数组,再用数组方法加工,必要时用Object.fromEntries还原。

sumSalaries的官方解答还给出了更精简的reduce写法(见 01-sum-salaries/solution.md):

function sumSalaries(salaries) { return Object.values(salaries).reduce((a, b) => a + b, 0); // 650 }

这与count的Object.keys(obj).length思路同源:都是"先转数组,再用数组原生能力一步算完"。

用测试验证行为:仓库中的真实断言

仓库为每个练习都配备了基于 Mocha 风格的测试文件(test.js),以describe/it/assert组织,完整覆盖了本任务的全部约定:

describe("count", function() { it("counts the number of properties", function() { assert.equal( count({a: 1, b: 2}), 2 ); }); it("returns 0 for an empty object", function() { assert.equal( count({}), 0 ); }); it("ignores symbolic properties", function() { assert.equal( count({ [Symbol('id')]: 1 }), 0 ); }); });

三个断言分别锁定三个行为契约:

  1. 常规计数:{a: 1, b: 2}有两个字符串键,结果为2;
  2. 空对象:{}结果为0;
  3. 忽略 Symbol 键:{ [Symbol('id')]: 1 }结果为0。

对照这三条断言,Object.keys(obj).length的实现全部通过,这也说明一行解法并非投机取巧,而是与任务的语义约定精确对齐。

小结

  • 统计对象"常规"属性的数量,标准且最简的写法是Object.keys(obj).length,见 solution.js。
  • 该写法成立的三层前提:Object.keys返回真正的数组(有.length)、遍历约定与for..in一致、自动忽略 Symbol 键——分别对应 article.md 中的三处说明。
  • 空对象与 Symbol 键两个边界无需手写分支,由 API 语义天然保证,测试用例 test.js 即为验证依据。
  • 若需统计 Symbol 键,可改用Reflect.ownKeys(obj)或Object.getOwnPropertySymbols(obj);姊妹任务 01-sum-salaries 展示了Object.values用于求和的同类思路。

掌握"对象 ↔ 数组"的双向转换(Object.keys/values/entries+ 数组方法 +Object.fromEntries),你就能用几行简洁代码处理绝大多数对象数据统计与转换需求。

  • 文档
  • 教程
  • 前端

【免费下载链接】zh.javascript.info

现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。

项目地址:https://gitcode.com/gh_mirrors/zh/zh.javascript.info
点击查看免费下载
上一篇:FlexSearch Resolver深入解析:用and、or、not、boost构建复杂布尔搜索与结果加权
下一篇:Notepad-- 快速上手指南:支持目录查找与文件对比的跨平台文本编辑器

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询