- 教育
【免费下载链接】JavaScript
Algorithms and Data Structures implemented in JavaScript for beginners, following best practices.
本文围绕 TheAlgorithms/JavaScript 仓库的 CONTRIBUTING.md 展开,系统拆解该开源项目的贡献流程、算法模块设计准则、提交信息规范、测试体系与代码风格要求。读完本文,你将掌握一套可复用的 JavaScript 算法库协作范式:如何写出符合 ES Module 规范、带异常处理、可通过 Vitest 单测验证且通过 Prettier 风格检查的算法模块,并顺利提交 Pull Request。
贡献前的准备:先通读,再提问
CONTRIBUTING.md 开门见山地强调:在提交任何 Pull Request 之前,必须完整阅读整份指南。这一要求并非走过场——仓库里既有fix:级别的零散改动,也有全新的算法实现,每一类改动都有对应的规范约束。
如果在阅读指南后仍有疑问,文档给出了两条求助通道:
- 在仓库的 Issues 区域清晰陈述你的疑问(说明你遇到的具体场景与问题,而不是泛泛提问);
- 加入项目的 Discord 社区,与维护者和其他贡献者直接交流。
从仓库现状看,这份指南确实是协作的"地基":README.md 中明确写着"在向本仓库贡献之前,请先阅读贡献指南",并将 CONTRIBUTING.md 列为必读文档。换言之,未读指南就提交 PR,很可能因为风格、命名或提交信息不合规而被要求返工。
贡献者契约:原创、许可与质量底线
在开始写代码之前,先明确贡献者需要认同的三条契约:
- 原创性:你的工作必须是自己完成的,抄袭不被允许,且任何抄袭内容都不会被合并;
- 许可协议:PR 合并后,你的工作将基于GNU GPLv3.0许可分发(仓库 LICENSE 与此对应,
package.json中声明的 license 字段同样是GPL-3.0); - 质量要求:提交的内容必须符合仓库的风格与标准。
同时,文档明确了欢迎什么、不欢迎什么:
| 类型 | 说明 |
|---|---|
| 新实现 | 欢迎全新的算法/数据结构实现,例如同一问题的不同解法、图的不同表示方式、不同复杂度的算法设计 |
| 注释改进 | 欢迎改进既有实现的注释质量 |
| 测试补充 | 欢迎为算法编写正确的单元测试 |
| 语法修正 | 哪怕只是修正拼写错误,也是被认可的贡献 |
另外一个对维护者很友好的约定:如果你的 PR 是为了解决某个已存在的 Issue,请在提交信息中加入fixes: #{$ISSUE_NO}标签({$ISSUE_NO}替换为真实 Issue 编号)。GitHub 会在 PR 被合并时依据该标签自动关闭对应 Issue,帮助社区保持 Issue 列表整洁。
什么是"合格"的算法模块:定义与设计准则
CONTRIBUTING.md 给出了一个精确定义:一个算法是一个或多个函数(或类),它必须满足:
- 接收一个或多个输入;
- 执行内部计算或数据操作;
- 返回一个或多个输出;
- 副作用尽可能小(minimal side effects)。
算法应以"便于读者将其嵌入更大程序"的方式打包。对算法本身还有四条硬性要求:
- 命名直观:类名和函数名应能让人一眼看出用途;
- 遵循 JS 命名习惯:使用符合 JavaScript 惯例的变量名,降低阅读成本;
- 输入灵活:算法应能接受不同的输入值;
- 错误即异常:输入非法时抛出 JavaScript 异常(如
RangeError),而不是静默返回错误结果。
源码印证:异常处理的两类典型写法
仓库中大量实现都贯彻了"非法输入抛异常"的原则。看 Maths/Factorial.js 的输入校验:
const calcFactorial = (num) => { if (num === 0) { return 1 } if (num < 0) { throw Error('Sorry, factorial does not exist for negative numbers.') } if (!num) { throw Error( 'Sorry, factorial does not exist for null or undefined numbers.' ) } // ... }负数和空值直接抛错,对应的测试 Maths/test/Factorial.test.js 用expect(() => calcFactorial(-1)).toThrow(Error)验证了这一行为。
再看 Backtracking/RatInAMaze.js 中更完整的校验:构造函数入口先调用validateGrid,依次检查"非空数组""正方形网格""单元格只能为 0 或 1",任何一项不满足都抛出TypeError:
function validateGrid(grid) { if (!Array.isArray(grid) || grid.length === 0) throw new TypeError('Grid must be a non-empty array') const allRowsHaveCorrectLength = grid.every( (row) => row.length === grid.length ) if (!allRowsHaveCorrectLength) throw new TypeError('Grid must be a square') const allCellsHaveValidValues = grid.every((row) => { return row.every((cell) => cell === 0 || cell === 1) }) if (!allCellsHaveValidValues) throw new TypeError('Grid must only contain 0s and 1s') }这种"构造函数阶段完成全部校验,之后公共方法只查询结果状态"的设计(见RatInAMaze类实现)正是文档所说"算法便于嵌入更大程序"的实践范本。
一个原则性限制:不要"复读"现有 npm 包
文档特别强调:仓库中的算法不应成为既有 npm 包的 how-to 示例。算法应自行完成内部计算与数据转换,虽然可以借用 JS 包中的数据类型、类或函数,但每个算法必须提供独特价值——否则就只是 API 文档的搬运工。
Commit 规范:全程遵循 Conventional Commits
提交信息必须遵循Conventional Commits规范,核心是使用下表的前缀(文档明确允许存在其他杂项前缀):
| 前缀 | 适用场景 |
|---|---|
fix: | 修复算法、工作流、配置/设置等中的 Bug |
feat: | 新增功能,如新算法、新工作流 |
docs: | 文档变更或修正,如改进贡献指南、修正拼写 |
test: | 修正既有测试或新增测试 |
chore: | 不属于以上任何类别的杂项变更 |
文档给出的最佳实践示例:
fix: fixed error in XYZ algorithm feat: re-work the CI workflow docs: improve the contributing guidelines test: add self-tests for XYZ algorithm chore: update readme badges从 package.json 可以看到,仓库还通过prepare: husky install在安装依赖时挂载 Husky Git Hooks——这意味着提交阶段很可能有自动校验(例如检查提交信息格式、运行风格检查),规范的 commit 信息是 PR 快速通过评审的前提。
文件命名与模块系统:UpperCamelCase + ES Module
命名规则
文件名必须遵循UpperCamelCase(PascalCase)风格,且不允许包含空格。文档给出的对照示例:
- ✅ 允许:
UserProfile.js - ❌ 不允许:
userprofile.js、Userprofile.js、user-Profile.js、userProfile.js
环顾仓库目录,这一规则被严格贯彻:例如 Data-Structures/Linked-List/SinglyLinkedList.js、Sorts/QuickSort.js、Search/BinarySearch.js 均为首字母大写、单词相接。
模块系统:ES Module(import/export)
仓库统一采用ES Module标准模块系统:使用export/import语句,而不是module.exports/require()。这一点与 package.json 中"type": "module"的声明完全一致——Node.js 会把整个包视为 ESM 项目。
仓库中的导出写法多样,但都遵循 ESM 语法:
- 具名导出函数:Sorts/BubbleSort.js 中
export function bubbleSort(items) { ... } - 导出类:Data-Structures/Tree/BinarySearchTree.js 以
export { Tree }收尾 - 具名导出常量:
Maths/Factorial.js中的export { calcFactorial }
测试文件同样用import引入被测模块,例如 Backtracking/tests/RatInAMaze.test.js 第一行就是import { RatInAMaze } from '../RatInAMaze'。
测试体系:Vitest 与"模块不含 live 代码"原则
文档强调测试的重要性:"写测试能确保实现在多次修复和变更后依然严密"。仓库使用Vitest作为单元测试框架(vitest.config.ts 中启用了globals: true与restoreMocks: true,并配置了 text/json/html 覆盖率报告)。
核心原则:算法模块只导出,不执行
文档明确建议:算法文件(模块)不应包含任何"live code",只导出执行算法所需的函数。测试代码负责导入这些函数、用合适的参数调用并检查输出。文档直接给出的范例正是 Backtracking/tests/RatInAMaze.test.js。
看该测试的实际结构,它完整演示了"错误路径 + 正确路径"的测试写法:
import { RatInAMaze } from '../RatInAMaze' describe('RatInAMaze', () => { it('should fail for non-arrays', () => { const values = [undefined, null, {}, 42, 'hello, world'] for (const value of values) { expect(() => { new RatInAMaze(value) }).toThrow() } }) it('should work for a simple 3x3 maze', () => { const maze = new RatInAMaze([ [1, 1, 0], [0, 1, 0], [0, 1, 1] ]) expect(maze.solved).toBe(true) expect(maze.path).toBe('RDDR') }) // ... })这份测试覆盖了六类场景:非法输入抛错(非数组、空数组、非方阵、含 0/1 之外的值)、单格迷宫可解/不可解、3x3 简单迷宫、2x2 不可解迷宫、更复杂的 7x7 迷宫及其不可解变体——堪称"测试分层"的教科书示例。
常用命令速查
文档给出了一套本地测试工作流(与 package.json 的 scripts 一一对应):
# 1. 先安装所有依赖 npm install # 2. 提交前在本地运行全部测试(对应 vitest run) npm test # 3. 只运行文件名包含 "koch" 的测试(无需指定文件夹路径) npm test -- koch # 4. 进入 watch 模式:监听源码与测试文件变更,改动即重跑(对应 vitest) npm run test-watchnpm test -- koch这种按文件名过滤的方式非常实用:当你新增了 Recursive/KochSnowflake.js 的测试时,无需跑完整个仓库的数百个用例,一条命令即可精准验证。
禁止使用 console
实现代码与测试代码中都禁止使用console方法(console.log等)。这保证了测试输出干净、CI 日志可读,也提醒贡献者:调试完成后必须清理调试语句,而不是让它们留在最终提交里。
编码风格:Prettier 与一组硬性约定
为了仓库整体一致性与可读性,新提交必须遵循Prettier风格。提交前运行:
npm run style这条命令对应 package.json 中的npx prettier . --write,会自动格式化整个仓库;同目录下还有npm run check-style(npx prettier . --check)可用于 CI 中检查格式是否符合要求。
关键约定速览
| 约定 | 要求 |
|---|---|
| 标识符命名 | camelCase,首字母小写(变量与函数) |
| 名字开头 | 必须以字母开头 |
| 缩进 | 代码块统一使用 2 空格缩进 |
| 全局变量 | 禁止使用全局变量 |
| 相等判断 | 禁止使用==(应用===) |
| 变量声明 | 使用let而非var |
| console | 禁止console.log及任何 console 方法 |
| alert | 绝对禁止使用alert |
| 语言版本 | 强烈推荐使用 ECMAScript 6(ES6) |
| 外部库 | 基础算法禁止导入外部库;仅复杂算法可少量使用 |
文档给出的规范代码示例(注意 2 空格缩进、let、===):
function sumOfArray(arrayOfNumbers) { let sum = 0 for (let i = 0; i < arrayOfNumbers.length; i++) { sum += arrayOfNumbers[i] } return sum }这套风格在仓库源码中处处可见:例如 Sorts/BubbleSort.js 使用let声明noSwaps、2 空格缩进,并借助if (noSwaps) break做了冒泡排序的优化;Backtracking/RatInAMaze.js 的递归函数同样严格遵守 camelCase 命名与 2 空格缩进。
最重要的约定:一致性
文档在"Happy coding!"之前留下最后一条、也是最重要的一条:提交时保持这些准则的使用一致性。风格不统一比风格"丑"更伤害可维护性——这也是 Prettier 被选为格式化工具的原因:自动化消除人为的风格分歧。
从零到一的贡献路径总结
把整份指南串成一条可执行的贡献流水线:
- 通读指南(本文已替你梳理完毕),有疑问走 Issue 或 Discord;
- 确认契约:原创、GPLv3.0 分发、符合风格标准;
- 选择贡献点:新算法实现、注释改进、测试补充皆可;若解决既有 Issue,在 commit 中加
fixes: #编号; - 编写模块:ES Module 语法(
export/import)、UpperCamelCase 文件名、类/函数命名直观、非法输入抛异常、不引入"live code"; - 编写测试:新建
xxx.test.js,导入模块函数并覆盖正常与异常路径,全程不使用console; - 本地验证:
npm install→npm test(或npm test -- 关键字精准过滤)→npm run style自动格式化; - 提交与 PR:提交信息遵循 Conventional Commits 前缀(
fix:/feat:/docs:/test:/chore:),等待维护者评审。
这套流程不仅适用于本仓库——"ESM 模块 + 纯导出函数 + 异常优先的输入校验 + 完整的单测矩阵 + 自动化格式检查"本身就是一份值得迁移到任何 JavaScript 算法库/工具库的高质量工程模板。你在 TheAlgorithms/JavaScript 中贡献的每一行代码,最终都会成为全球学习者的教材,这正是这份指南存在的意义。
- 教育
【免费下载链接】JavaScript
Algorithms and Data Structures implemented in JavaScript for beginners, following best practices.
相关推荐
degit 贡献指南:从 Bug 报告、首次代码贡献到 Conventional Commits 提交规范
degit 贡献指南:从 Bug 报告、首次代码贡献到 Conventional Commits 提交规范 本文基于 docs/CONTRIBUTING.md
开发工具CLI从本地树形界面到远程 Web 页面:LibreHardwareMonitor 硬件监控上手指南
从本地树形界面到远程 Web 页面:LibreHardwareMonitor 硬件监控上手指南 渲染到一半,机器风扇突然全速运转,但你说不清是 CPU、显卡还是
指标监控gopass 开源贡献指南:从 Conventional Commits 到构建、测试与发布全流程
gopass 开源贡献指南:从 Conventional Commits 到构建、测试与发布全流程 本文是 gopass(面向团队的 Unix 密码管理器)开源
应用安全开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考