拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TheAlgorithms/JavaScript 贡献指南全解读:从 Conventional Commits 到 Vitest 测试与 Prettier 编码规范

TheAlgorithms/JavaScript 贡献指南全解读:从 Conventional Commits 到 Vitest 测试与 Prettier 编码规范
  • 教育

【免费下载链接】JavaScript

Algorithms and Data Structures implemented in JavaScript for beginners, following best practices.

项目地址:https://gitcode.com/gh_mirrors/ja/JavaScript
点击查看免费下载

本文围绕 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)。

算法应以"便于读者将其嵌入更大程序"的方式打包。对算法本身还有四条硬性要求:

  1. 命名直观:类名和函数名应能让人一眼看出用途;
  2. 遵循 JS 命名习惯:使用符合 JavaScript 惯例的变量名,降低阅读成本;
  3. 输入灵活:算法应能接受不同的输入值;
  4. 错误即异常:输入非法时抛出 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-watch

npm 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 被选为格式化工具的原因:自动化消除人为的风格分歧。

从零到一的贡献路径总结

把整份指南串成一条可执行的贡献流水线:

  1. 通读指南(本文已替你梳理完毕),有疑问走 Issue 或 Discord;
  2. 确认契约:原创、GPLv3.0 分发、符合风格标准;
  3. 选择贡献点:新算法实现、注释改进、测试补充皆可;若解决既有 Issue,在 commit 中加fixes: #编号;
  4. 编写模块:ES Module 语法(export/import)、UpperCamelCase 文件名、类/函数命名直观、非法输入抛异常、不引入"live code";
  5. 编写测试:新建xxx.test.js,导入模块函数并覆盖正常与异常路径,全程不使用console;
  6. 本地验证:npm install→npm test(或npm test -- 关键字精准过滤)→npm run style自动格式化;
  7. 提交与 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.

项目地址:https://gitcode.com/gh_mirrors/ja/JavaScript
点击查看免费下载

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

返回列表