- 文档
- 教程
- 前端
【免费下载链接】zh.javascript.info
现代 JavaScript 教程(The Modern JavaScript Tutorial),以最新的 ECMAScript 规范为基准,通过简单但足够详细的内容,为你讲解从基础到高阶的 JavaScript 相关知识。
本练习来自现代 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; // 2Object.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 ); }); });三个断言分别锁定三个行为契约:
- 常规计数:
{a: 1, b: 2}有两个字符串键,结果为2; - 空对象:
{}结果为0; - 忽略 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 相关知识。
相关推荐
使用 Object.keys 统计 JavaScript 对象属性数量:count(obj) 实战任务解析
使用 Object.keys 统计 JavaScript 对象属性数量:count obj 实战任务解析 本篇技术指南以 Modern JavaScript T
文档/教程前端30 seconds of code 实战:用 Object.keys() 与 forEach 迭代 JavaScript 对象自有属性
30 seconds of code 实战:用 Object.keys 与 forEach 迭代 JavaScript 对象自有属性 对象在 JavaScrip
教程文档JavaScript 对象遍历实战:Object.keys / Object.values / Object.entries 详解与 Map 对比
JavaScript 对象遍历实战:Object.keys / Object.values / Object.entries 详解与 Map 对比 本篇指南以
文档/教程前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考