【源码阅读】omit.js|omit的实现与库的快速搭建

单词 omit 有「从中剔除,不包含」的释义,而 omit 工具函数的工作是「从对象中剔除指定属性作为新对象返回」,举一个例子

// 从哆啦A梦的信息中剔除掉「物种」和「生日」信息
const info = { name: "Doraemon", species: "Robot Cat", birthdate: "2112/09/03" }
const basicInfo = omit(info, ["species", "birthdate"]);

console.log(basicInfo); // { name: "Doraemon" }
console.log(info); // { name: "Doraemon", species: "Robot Cat", birthdate: "2112/09/03" } 

omit 是一个不可或缺的工具函数,lodash 和 underscore 中都有他的影子,今天阅读的 omit.js 则是对 omit 做了单独实现。

omit.js 的基本情况

以2023年1月17日为时间节点,omit.js 最近版本发布在 2020年7月8日,周下载量30w以上。

查看 Github 的贡献者列表和依赖 omit.js 的 package 列表,可以分析出 omit.js 是一个由蚂蚁集团前端大佬维护的包,并且仍旧应用于 @ant-design/pro-table @ant-design/pro-table 等项目。

如此一来可以得出结论:omit.js 是一个仍在维护中并处于长期稳定状态的项目。

omit 功能的实现

omit.js 的实现代码在 omit.js/index.js at…

function omit(obj, fields) {// eslint-disable-next-line prefer-object-spreadconst shallowCopy = Object.assign({}, obj);for (let i = 0; i < fields.length; i += 1) {const key = fields[i];delete shallowCopy[key];}return shallowCopy;
}

export default omit; 

非常简单,三个步骤

1.使用 Object.assign 生成一个浅拷贝对象 shalldowCopy
2.遍历指定属性列表,将其从 shalldowCopy 中删除
3.返回 shalldowCopy

在大多数的认知中 Object.assign 是通过浅拷贝合并生成对象,但这种说法其实并不准确。

Object.assign 真正的作用是将源对象上的所有可枚举自有属性复制到目标对象上。

实质上是调用源对象的 getter 拿到属性值,调用目标对象的 setter 设置属性值。

因此,Object.assign 并不是原来认知的全面复制,它不会去复制

  • 对象的原型
  • 对象实例上的不可枚举属性
  • 对象上的gettersetter访问器

同时它也会被目标对象的属性状况影响,例如

  • 设有 setter 的属性
  • 不可写的属性

举个例子

const obj = Object.create({ foo: 1 }, // 原型
{ bar: {value: 2 // 不可枚举},baz: {value: 3,enumerable: true // 可枚举},qux: {get () { return 4},enumerable: true // 可枚举}
});

Object.assign({}, obj); // {baz: 3, qux: 4} 

如果不考虑复杂结构,只需要复制不可枚举的自有属性,那Object.assign 完全可以满足。

为什么不使用 lodash 和 underscore

lodash 和 underscore 有更丰富的参数支持

const object = { "a": 1, "b": { c: 2, d: 3 } }
lodash.omit(object, "a"); // 单个属性
lodash.omit(object, ["a", "b"]); // 属性列表
lodash.omit(object, [["b", "c"]]); // 多层属性

underscore.omit(object, "a"); // 单个属性
underscore.omit(object, ["a", "b"]); // 属性列表
underscore.omit(object, function (val, key, obj) { // 筛选函数
	return val !== null && typeof val === "object";
}); 

为了实现更丰富的使用方式,同时支持处理各种类型的数据,lodashunderscore 的源码里做了大量额外工作。

如果输入类型单一并且只处理不可枚举的自有属性,那么没有必要使用 lodash、underscore 徒增复杂度。

omit.js包的开发和发布流程

从 omit.js 目录可以看出个大概,项目依赖 eslint、father、Travis CI,内置 TS 类型声明 index.d.ts,关键还是看 package.json

{"name": "omit.js","version": "2.0.2","description": "Utility function to create a shallow copy of an object which had dropped some fields.","main": "lib/index.js", // npm 包的入口:打包生成的 lib/index.js"module": "es/index.js", // npm 包 ESM 规范的入口:打包生成的 es/index.js"types": "index.d.ts", // npm 包的 TS 类型声明文件:index.d.ts"files": [ // npm 包中包含以下目录和文件"lib","es","dist","index.d.ts"],"scripts": {"start": "father doc dev --storybook", "build": "father doc build --storybook","compile": "father build","gh-pages": "father doc deploy","prepublishOnly": "npm run compile && np --yolo --no-publish","lint": "eslint .","test": "father test","coverage": "father test --coverage"}
} 

脚本命令表明 omit.js 的开发、打包构建、测试都离不开 father 包,我们先来研究一下 father 是何方神圣。

father v2:库开发脚手架

设想你要从零创建并维护一个优秀的前端库,考虑到高质量、可维护、高效开发,你的脑中冒出一些需求:

  • 开发流程要稳定又高效
  • 这个库得有靠谱的兼容性
  • 迭代开发过程中要尽量降低代码出 bug 的概率
  • 最好还能有易读的文档,特别是涉及 UI 的包(组件库之类)能让用户直接体验

然后对应到:开发环境、打包构建、代码规范、TypeScript 支持、单元测试环境、文档生成。

一系列基建的安装和配置横亘在面前,更要命的是当你开启下一个库的开发,一切又要再来一遍。

有没有简化这个过程的工具呢,有没有一个针对库开发的脚手架?

father2.x 将这些零散的需求合而为一,基于 rollup, docz, storybook, jest, prettier 和 eslint 提供全套基础服务。

father 目前已经迭代到 4.x 版本,但功能支持、使用方式相较 2.x 版本都有改变。这篇文章聚焦于 omit.js 项目,因此只关注 father2.x 版本的使用

father 的命令

father build # 打包 npm 包,rollup 作为打包器

father doc dev # 使用 docz 启动多用例独立的开发环境,非常适合组件库开发
father doc build # 使用 docz 编译生成文档
father doc deploy # 将文档部署到 github.io 上
father doc dev --storybook # 可选用 storybook 替代 docz

father test # 执行单元测试,由 umi-test 提供 jest 支持
father test --coverage # 执行单元测试并统计覆盖率 

另外,在首次执行 father 命令时,father 会往 git pre-commit hook 上挂载 lint 操作,这样在调用 git commit 时会主动走一遍 lint。

np:包发布辅助工具

手动的发包流程大致是:

1.编译打包生成产物
2.跑测试确认无误
3.修改 package.json 中的版本号
4.git add + git commit 提交暂存区中的所有代码
5.运行 npm publish 发包

然而这个过程,既不方便又容易出错,例如忘记跑测试、版本推算出错、忘记修改版本号、目录中还有未提交的代码;

还有一些意料之外的疏忽,例如远程仓库同一分支上有未拉取的代码、node_modules 中的项目依赖并不是最新的;

还有一些不好的发布习惯,例如在临时分支上发包、新版本发布后不打 tag。

np 包的工作就是修补这些问题,将上面这些需要留心的工作都交给工具,减少错误发生的概率。

omit.js 的脚本命令里,在 prepublishOnly 钩子上挂了 npm run compile && np --yolo --no-publish

--yoyo--no-publish 是 np 给的两个参数,分别表示:跳过依赖更新和代码测试、不要自动发包。

omit.js 这么做也能理解,跑 npm publish 进入 prepublishOnly → 编译代码 → 交给 np 做版本设置和一系列检查 → 检查通过进入 publish 发包

omit.js 开发构建和发布流程

 "scripts": {"compile": "father build", // 编译 index.js 生成不同规范的代码"prepublishOnly": "npm run compile && np --yolo --no-publish", // npm publish 触发编译和包发布流程"lint": "eslint .","test": "father test","coverage": "father test --coverage"}, 

1.编写 src/index.js 代码
2.运行 npm run test 跑测试
3.运行 git commit 本地 lint 后提交代码,远程走 CI 跑 lint、test 和 coverage
4.运行 npm publish 打包和发布

我学到了什么

omit.js 的作用是「从对象中剔除指定的可枚举自有属性」,使用 Object.assign 和属性删除做简单实现。

Object.assign 真正的作用是将源对象上的所有可枚举自有属性复制到目标对象上。实质上是调用源对象的 getter 拿到属性值,调用目标对象的 setter 设置属性值。

omit.js 项目的基建依赖于 father,包发布使用 np

  • father 是库开发的脚手架,father v4 相较于 father v2 有更多现代化的支持,例如加入 Bundleless。在创建一个新库时或许可以直接用 father 试试。
  • np 是包发布流程的辅助工具,它提供了版本设置和各种细致的检查,将发布流程中的各种细节自动化,是一个非常实用的工具。

最后

为大家准备了一个前端资料包。包含54本,2.57G的前端相关电子书,《前端面试宝典(附答案和解析)》,难点、重点知识视频教程(全套)。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

猜你喜欢

转载自blog.csdn.net/Android_boom/article/details/129425872