首页
/ freeCodeCamp 基础数据结构实战:用 JavaScript delete 关键字移除对象属性

freeCodeCamp 基础数据结构实战:用 JavaScript delete 关键字移除对象属性

2026-09-06 18:46:25作者:裘旻烁

本篇文章围绕 freeCodeCamp 开源课程 Basic Data Structures(基础数据结构)模块中的经典关卡 "Use the delete Keyword to Remove Object Properties"(关卡源文件)展开,系统讲解如何使用 delete 运算符从 JavaScript 对象中移除键值对,并结合该仓库的源码结构、测试断言与配置 schema,深入辨析 delete 与其它"删除"写法的本质差异。读完本文,你将理解对象作为键值存储的核心特性,掌握 delete 的语法、返回值与适用边界,并能独立完成该关卡及同模块后续挑战。

一、关卡定位:Basic Data Structures 模块中的关键一环

Basic Data Structures 模块结构文件 中,本关卡(id: 587d7b7c367417b2b2512b1bdashedName: use-the-delete-keyword-to-remove-object-properties)是整个模块第 16 个挑战。它在课程脉络上承接前面三道对象操作关卡,又为后续关卡做铺垫:

从代码结构看,这一系列关卡构成一条完整的学习链:增 → 改 → 查 → 删 → 再验证,本关卡正是链条中"删"的环节。

二、为什么要用 delete:回顾对象(Object)的本质

关卡描述中明确指出:对象本质上是 key-value store(键值存储),具备两大优势——为数据提供灵活、直观的组织方式,同时提供非常快的查找时间(very fast lookup time)

理解这一背景对正确使用 delete 很重要:

  1. 对象是引用类型:变量保存的是对象的引用,而非值本身;
  2. 键值对动态可变:属性可以随时添加、修改和删除,无需重新定义整个对象;
  3. 查找依赖键名哈希:这也是描述中强调"快速查找"的原因——对象访问本质上是基于键名的哈希查找,与数组基于下标顺序遍历不同。

关卡源码给了本节练习使用的 foods 对象(源文件 --seed-- 部分):

let foods = {
  apples: 25,
  oranges: 32,
  plums: 28,
  bananas: 13,
  grapes: 35,
  strawberries: 27
};

// Only change code below this line

// Only change code above this line

console.log(foods);

该对象的初始定义(含 let 声明)在前序关卡 Add Key-Value Pairs to JavaScript Objects--solutions-- 中曾经出现过:那里用 foods['bananas'] = 13; 等写法完成了"增",而本节要做的正是反向操作——"删"。

三、delete 运算符基础语法与示例

delete 是 JavaScript 的一元运算符,用于删除对象的自有属性(own property)。关卡给出的核心示例非常简洁:

delete foods.apples;

执行后,foods 对象中将不再存在 apples 这个键。它既支持点号写法,也支持方括号写法(当属性名含特殊字符或来自变量时必须用方括号):

delete foods['apples'];   // 方括号 + 字符串字面量,等价于 delete foods.apples;

const key = 'apples';
delete foods[key];        // 属性名来自变量,只能使用方括号

delete 的返回值

delete 表达式会返回一个布尔值,可以用它来判断删除是否成功:

  • 删除自有可配置属性成功时返回 true
  • 试图删除不存在的属性也会返回 true(JS 引擎将其视为"删除成功",因为目标属性原本就不存在);
  • 删除不可配置属性(如 var 声明的全局变量、内置对象的某些属性)时,在非严格模式下返回 false,在严格模式下会抛出 TypeError

一个"删不干净"的经典陷阱

delete 只能删除对象自身的属性,不能删除继承自原型链的属性。如果某属性来自构造函数的 prototype 或内置原型,delete 对该实例是无效的,属性会依然可见。这也解释了为什么后续关卡 Check if an Object has a Property 的官方解法特意选用 hasOwnProperty()(自有属性检查)来精确判断"实例自身是否拥有该键"。

从源码层面看,关卡测试中大量使用 !foods.hasOwnProperty('oranges') 这类断言(见下节),说明本关卡的教学目标是让学习者理解:真正"移除"一个键应使用 delete,其效果可以用 hasOwnProperty()Object.keys() 进行验证。

四、实战任务与自动化测试断言解析

关卡 --instructions-- 要求:

Use the delete keyword to remove the oranges, plums, and strawberries keys from the foods object.

在 freeCodeCamp 课程体系中,每一道关卡都配有自动化测试(--hints--),对应仓库中的 schema 校验与构建逻辑。本关卡的测试分两组,拆解如下。

断言一:删除效果的可观测验证

assert(
  !foods.hasOwnProperty('oranges') &&
    !foods.hasOwnProperty('plums') &&
    !foods.hasOwnProperty('strawberries') &&
    Object.keys(foods).length === 3
);

这条断言同时验证两件事:

  • foods 不再自有 orangesplumsstrawberries 三个键(用 hasOwnProperty 排除原型链干扰,精确判断是自有属性删除而非被继承遮蔽);
  • 剩余键数量恰好为 3(Object.keys(foods).length === 3),即只剩下 applesgrapesbananas

注意:如果只是把属性值设为 undefined(例如 foods.oranges = undefined),属性键仍然存在,hasOwnProperty('oranges') 依旧返回 trueObject.keys(foods).length 仍会是 6,无法通过该断言。这从侧面说明 delete 与"赋值为 undefined"在语义上的本质区别。

断言二:确认使用的是 delete 写法

assert(
  __helpers.removeJSComments(code).search(/oranges:/) !== -1 &&
    __helpers.removeJSComments(code).search(/plums:/) !== -1 &&
    __helpers.removeJSComments(code).search(/strawberries:/) !== -1
);

__helpers.removeJSComments(code) 是课程测试运行环境提供的辅助函数,用于剔除代码注释后再做正则匹配。该断言用于确认:学习者没有改动 foods 对象的初始定义(即 oranges:plums:strawberries: 键仍存在于对象字面量中),改动只应发生在对象字面量之外的 delete 语句上。这也呼应了"只修改指定区域"的工程实践——初始数据保持不变,删除操作通过独立语句完成。

五、参考答案与代码运行结果

关卡 --solutions-- 中给出的官方参考实现如下:

let foods = {
  apples: 25,
  oranges: 32,
  plums: 28,
  bananas: 13,
  grapes: 35,
  strawberries: 27
};

delete foods.oranges;
delete foods.plums;
delete foods.strawberries;

console.log(foods);

运行后控制台输出:

{ apples: 25, bananas: 13, grapes: 35 }

可以看到三个目标键被移除,其余键值对完好保留。相比重建一个对象(手动剔除不想保留的键),delete 的语义是原地修改,代码意图一目了然;而在需要频繁删除属性的动态场景(如缓存清理、表单状态重置)中,这种原地操作也避免了不必要的对象复制。

六、delete 的边界条件与实战注意事项

基于 JavaScript 语言语义与本关卡的测试逻辑,使用 delete 时需要记住以下边界条件:

1. 对变量声明无效

varletconst 声明的变量本身(包括全局变量)通常是不可配置的,delete 不能把它们从作用域中移除,非严格模式下静默返回 falsedelete 的操作对象是"对象的属性",而不是"变量"。

2. 对数组会产生"空洞"

虽然本关卡主题是对象,但需要留意:对数组使用 delete(如 delete arr[1])只会把该下标位置置为"空槽(hole)",数组的 length 不会缩短。若想真正移除数组元素,应使用 splice() 等数组专用方法——这正是 Basic Data Structures 模块前序关卡 Remove Items Using splice() 的内容,两者恰好形成对照。

3. 与置空(set to undefined/null)的区别

操作 键是否还在 hasOwnProperty Object.keys 计数 适用场景
delete obj.key 否(键被移除) false 减少 真正删除结构成员、清理缓存
obj.key = undefined 是(键保留,值为 undefined) true 不变 临时清值、避免频繁增删键

4. 非严格模式 vs 严格模式

对不可配置属性执行 delete 时:非严格模式返回 false 不报错;严格模式下直接抛出 TypeError。建议在模块化代码中以严格模式运行,让这类错误尽早暴露。

5. 性能语义提示

关卡描述强调对象提供"非常快的查找时间"。作为键值存储的对象删除键值对是常数级语义操作,但如果需要在热路径中反复增删同一批属性,也可以考虑"重新赋值新对象 + 解构排除"等不可变风格写法,二者各有利弊,应根据真实场景(可变共享引用 vs 数据不可变)权衡。

七、从课程文件到题库:仓库里如何组织这道题

这道题只是仓库课程数据中的一个节点,理解它在整个体系中的存放方式,有助于你阅读并复用整套课程数据:

  • 关卡正文:存放在 curriculum/challenges/english/blocks/basic-data-structures/587d7b7b367417b2b2512b1b.md,文件名即关卡唯一 id。每个关卡文件都包含 frontmatter(YAML 头:idtitlechallengeTypeforumTopicIddashedName)与 --description----instructions----hints----seed--(含 --seed-contents--)、--solutions-- 等结构化区块。
  • 模块结构curriculum/structure/blocks/basic-data-structures.json 定义了该 block 的 challengeOrder(本关卡排在第 16 位)、helpCategory: "JavaScript" 以及 blockLayout: "legacy-challenge-list" 布局方式。
  • 类型体系:本关卡 frontmatter 中 challengeType: 1 对应 packages/shared/src/config/challenge-types.ts 中的 const js = 1;,即经典的"JavaScript 单文件挑战"类型,配备可编辑的 --seed-contents-- 与浏览器内测试。
  • Schema 约束curriculum/schema/challenge-schema.js 使用 Joi 校验所有关卡元数据,例如 challengeType: Joi.number().min(0).max(33).required()description 对不同类型关卡的必填/允许为空规则,以及 helpCategory 的取值白名单(其中包含 'JavaScript')等,保证本文件这类课程数据在进入构建与测试管线前格式合法。

如果你在本机克隆了本仓库,即可在 curriculum/challenges/english/blocks/basic-data-structures/ 目录下打开该关卡文件,替换 --seed-contents-- 区域内的实现后,通过课程配套的挑战测试来校验自己的答案是否通过全部 hint。

八、小结:从"会删"到"删得明白"

本关卡篇幅虽短,却承载了三个必须透彻理解的知识点:

  1. 语法层面delete object.property(或 delete object['property'])原地移除对象的自有键值对;
  2. 语义层面delete 针对属性而非变量,对数组产生空洞而非缩短长度,删除不可配置属性在严格模式会抛错;
  3. 验证层面:删除是否生效,要用 hasOwnProperty()Object.keys() 来检验——这也正是本关卡 hint 断言的设计逻辑,并为下一关 Check if an Object has a Property 埋下伏笔。

掌握 delete,就等于在"增、改、查、删"的对象操作闭环中补上了最后一块拼图;配合 Iterate Through the Keys of an Object with a for...in StatementGenerate an Array of All Object Keys with Object.keys() 等后续关卡,你将能完整掌握对象这一 JavaScript 核心数据结构的增删改查与遍历技巧。

登录后查看全文
热门项目推荐
相关项目推荐