- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
本篇技术指南以 Modern JavaScript Tutorial(本仓库1-js教程体系)中的实战任务为核心,讲解如何编写一个简洁的count(obj)函数来统计普通对象的属性数量。文章将完整还原任务要求、标准解法与配套测试用例,并深入剖析Object.keys/Object.values/Object.entries三兄弟的底层行为(包括它们与for..in一样会忽略 Symbol 键这一关键特性),帮助读者理解"为什么Object.keys(obj).length是最短且正确的答案"。
任务背景:它属于哪一章
本任务是 JavaScript 基础部分"数据类型(Data Types)"章节中 Object.keys, values, entries 一节的配套练习。该章节在讲完Map、Set、Array的迭代方法之后,专门介绍普通对象的三个静态方法:
Object.keys(obj)—— 返回由对象所有键组成的数组;Object.values(obj)—— 返回由对象所有值组成的数组;Object.entries(obj)—— 返回[key, value]键值对数组。
与Map的map.keys()这种实例方法语法不同,普通对象必须用静态调用形式Object.keys(obj),且返回的是"真正"的数组(而非可迭代对象)。关于二者的差异,章节中给出了清晰的对照表:
| Map | Object | |
|---|---|---|
| 调用语法 | map.keys() | Object.keys(obj),而不是obj.keys() |
| 返回值 | 可迭代对象 | "真正的"数组 |
任务原文:count(obj)
任务文件位于 02-count-properties/task.md,要求如下:
编写一个函数count(obj),返回对象中属性的数量:
let user = { name: 'John', age: 30 }; alert( count(user) ); // 2同时附带两条要求:
- 让代码尽可能短;
- 忽略 Symbol 类型的属性,只统计"常规"属性(P.S. 提示语)。
这是一个重要性标记为importance: 5的核心练习,考察的正是Object.keys的使用与对"Symbol 键被忽略"这一语义的掌握。
标准解法:一行代码
仓库中配套的标准解法位于 02-count-properties/_js.view/solution.js,完整代码如下:
function count(obj) { return Object.keys(obj).length; }这个解法之所以是"最短且正确"的,原理链条如下:
Object.keys(obj)返回一个包含对象自身可枚举字符串属性名的数组;- 由于
Object.keys与for..in一样会忽略以Symbol(...)为键的属性(详见章节中的 warn 提示),因此统计结果天然满足"忽略 Symbol 属性"的要求; - 对数组取
.length即为属性数量。
需要说明的是,Object.keys只统计自身的可枚举字符串属性,不含原型链上的继承属性。对本题这样的普通字面量对象而言,这恰好就是我们想要的行为。
测试用例:三种边界验证
仓库为每个任务都提供了配套的 Mocha 测试,本任务的测试位于 02-count-properties/_js.view/test.js,共覆盖三个典型场景:
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; - 空对象边界:
{}没有任何属性,Object.keys({}).length为0,这保证函数对空对象不报错; - Symbol 键忽略:
{ [Symbol('id')]: 1 }中唯一属性是 Symbol 键,结果为0,验证了任务 P.S. 中的核心要求。
从源码结构看,_js.view目录是任务的"可运行视图",其中solution.js为被测试的实现、test.js为断言脚本,二者共同构成了读者可以在浏览器中直接运行的练习环境。
深入理解:为什么 Symbol 键被忽略
任务 P.S. 特意强调"忽略 symbolic properties",这是本练习的隐藏考点。在 article.md 的 warn 提示中,教程明确指出:
就像
for..in循环一样,Object.keys/Object.values/Object.entries会忽略使用Symbol(...)作为键的属性。通常这很方便。但如果我们也需要 Symbol 键,那么有一个单独的方法Object.getOwnPropertySymbols(obj)返回只包含 Symbol 键的数组;此外还有Reflect.ownKeys(obj)可以返回所有键。
因此在实际开发中,针对"统计属性数量"的需求要按场景选择:
| 需求场景 | 推荐方法 | 返回值示例 |
|---|---|---|
| 只统计常规字符串属性 | Object.keys(obj).length | 字符串键的个数 |
| 只统计 Symbol 属性 | Object.getOwnPropertySymbols(obj).length | Symbol 键的个数 |
| 统计全部自身属性(含 Symbol 键) | Reflect.ownKeys(obj).length | 字符串键 + Symbol 键的总数 |
延伸练习:Object.values 与求和
同一个章节还包含一个姊妹任务 01-sum-salaries:用Object.values和for..of循环求对象中所有薪水之和,空对象返回0。其标准解法在 01-sum-salaries/solution.md:
function sumSalaries(salaries) { let sum = 0; for (let salary of Object.values(salaries)) { sum += salary; } return sum; // 650 }教程还给出一种更精简的替代写法——Object.values配合数组的reduce方法:
function sumSalaries(salaries) { return Object.values(salaries).reduce((a, b) => a + b, 0); // 650 }这两个任务合在一起,恰好演示了Object.keys与Object.values的典型组合用法:前者用于获取结构信息(键的个数),后者用于遍历值做聚合计算。
进阶技巧:entries 与 fromEntries 组合变换
章节中Object.entries的一个重要用途是配合Object.fromEntries对对象做"数组化变换"。例如把价格对象中的所有价格翻倍:
let prices = { banana: 1, orange: 2, meat: 4, }; let doublePrices = Object.fromEntries( Object.entries(prices).map(entry => [entry[0], entry[1] * 2]) ); alert(doublePrices.meat); // 8这个三步走的模式(Object.entries取出键值对数组 → 用数组方法map/filter变换 →Object.fromEntries还原为对象)弥补了普通对象没有map、filter等数组方法的短板。熟悉它之后,就能像处理数组一样对对象做任意链式变换,这也是本练习所在章节的进阶价值所在。
小结
count(obj)的标准答案是一行代码:Object.keys(obj).length;Object.keys只返回自身可枚举的字符串键,天然忽略 Symbol 键,正好满足任务 P.S. 的要求;- 仓库配套测试(test.js)覆盖常规计数、空对象、Symbol 忽略三种边界,可用于验证实现的正确性;
- 如需统计 Symbol 键,可改用
Object.getOwnPropertySymbols;如需统计全部键,可改用Reflect.ownKeys; - 同章节的
Object.values/Object.entries分别擅长聚合求值与"数组化变换",配合Object.fromEntries可完成强大的对象处理链。
- 文档/教程
- 前端
【免费下载链接】en.javascript.info
Modern JavaScript Tutorial
相关推荐
Lance Rust 核心开发规范:代码风格、并发边界、API 设计与错误处理实战指南
Lance Rust 核心开发规范:代码风格、并发边界、API 设计与错误处理实战指南 导读 rust/CLAUDE.md https://link.gitco
文档/教程前端Watch.JS基础使用教程:实现JavaScript对象属性监听
Watch.JS基础使用教程:实现JavaScript对象属性监听 什么是Watch.JS Watch.JS是一个轻量级的JavaScript库,它允许开发者监
软件架构开发工具如何为ESP32 AI聊天机器人注入情感灵魂:xiaozhi-esp32表情系统深度解析
如何为ESP32 AI聊天机器人注入情感灵魂:xiaozhi esp32表情系统深度解析 当冰冷的硬件设备遇上温暖的情感表达,会发生什么奇妙的化学反应?这正是x
人工智能大模型语音交互助手嵌入式物联网智能硬件MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考