作者简介: hustcc 蚂蚁金服. 数据体验技术团队
immutable 是什么? 不变的一成不变的在 Javascript 中一般指一个变量在经过一个 function 处理之后, 可以保持入参数据不变
一什么是 immutable?
举个真实日常例子: 前两天, 在业务代码中, 需要获得数据的中位数, 并使用 echarts 绘制出中位线, 辅助分析写代码之前, 先找了一下中位线的定义
对于有限的数集, 可以通过把所有观察值高低排序后找出正中间的一个作为中位数如果观察值有偶数个, 通常取最中间的两个数值的平均值作为中位数
哦, 挺简单, 于是奋笔疾书(其实是 github 上的代码片段)
- /**
- * 求取数组中的中位数
- * [1, 2, 3, 4, 4] -> 3
- * [1, 2, 3, 4, 5, 6] -> 3.5
- */
- const calculateMedian(arr) => {
- // 1. 排序
- arr.sort((a, b) => a - b);
- const half = Math.floor(arr.length / 2);
- // 2. 按照中位数定义计算
- // 奇数长度则中间值, 偶数长度则中间两个数的平均值
- return arr.length % 2 !== 0 ?
- arr[half] :
- (arr[half - 1] + arr[half]) / 2.0;
- }
这段代码的问题在于函数是 mutable 的:
当经过函数 calculateMedian 求取数组 [5, 4, 3, 2, 1] 的中位数的时候, 导致入参数组变成了 [1, 2, 3, 4, 5]导致后续的代码拿到的数组, 都是经过排序的, 数据都是混乱的
上述代码只需要修改一个地方即可:
const newArr = [].concat(arr).sort((a, b) => a - b);
其中 [].concat(arr) 达到的效果就是代码的 immutable
通过这样的一个实际例子, 我想应该很容易理解 mutable 代码造成的影响是什么?
数据污染: 经过一个方法, 数据就变化了, 多么可怕
数据不可控: 数据不仅仅是被修改, 而且我们没法控制数据被修改的规律
debug 黑洞: 一般的 bug, 如果了解业务, 可能不用 debugger 就能定位一二; mutable 产生的 bug, 没办法, 新增代码一句句 F10 吧
二常见的原因
很多情况下, 道理我们都懂, 但是还是会不经意写出 mutable 的代码分为几种情况, 看起来清晰一些吧!
1. 意识不清晰
很多开发者在写代码的时候, 没有从全局胜寒意识到函数必须保证 immutable, 只在乎自己的函数输入输出符合要求
开发者需要有时刻警惕的意识, 一旦涉及到 object 的操作, 先提醒自己注意不可变特别是对于公共模块的开发, 开源项目的开发, 养成变道就打转向灯的好习惯
2. 函数理解不清晰
Array 常见的函数 pushpopshift 等, 一般都能理解它们是会修改原数组数据, 是 mutable 的函数
但是对于上述例子中的 sort 函数应该就是理解上容易产生歧义 (自己之前一直以为是生成一个新的数组, 写完这篇, 我去搜索了整个业务代码) 同样的, 容易产生歧义的函数包括:
- sort
- reverse
- fill
- splice
- delete
注意: Array 中 immutable 函数很多, 以上几个函数是功能上容易误解的
3. 使用不靠谱的开源库
GitHub 真是个好东西, 只要你能想到的代码, 基本都有参考的, 特别是 JavaScript 语言的
但是你永远不知道你使用的轮子是不是仅仅是一个课堂作业练手的在使用一个开源模块的时候, 需要观察:
单测是否完善: 不是指单测是不是绿色(pass), 而是看单测代码是否靠谱
在完善的单测可以看到:
项目实际的使用方式, 这些很可能是 README 中无法完整写出来的东西
理解项目的模块划分, 组织架构
开发者在项目代码中扣住的开发细节点
比如, 如果 immutable 是项目的一个重要特性之一, 那么在单测代码中一定会反映出来
npm 下载量
就像在网上购物一样, 比较懒的购物者, 直接买订单数最多的爆款
issues
可以关注在 issue 处理速度 issue 中搜索关键字, 可以大概知道是否存在这些问题
何妨 review 一下
如果代码简单, 可以简单 review 一下, 抓住代码实现的关键点
三如何写 immutable 的代码
既然要写 immutable 的代码, 那我们应该怎么做?
1. 靠谱开源项目
immutability-helper: 基于原始 Array 和 Object 的 immutable 操作, 很好用
js-joda: 日期 Date immutable 方法
immutable-js:Facebook 的 immutable 模块, 定义了新的数据结构, 使用成本相对高一点点
immutability-helper-x: 包装 immutable-helper, 使用更友好
immu: 基于 Proxy 的 immutable 模块, 思路新颖, 但是兼容性差一点
2. 单测约束
针对开头的 calculateMedian 函数, 写一个单测:
- describe('calculateMedian', () => {
- test('immutable', () => {
- const a = [5, 4, 3, 2, 1];
- const aClone = a.slice();
- expect(calculateMedian(a)).toBe(3);
- // 保证不能被修改!
- expect(a).toEqual(aClone);
- });
- });
单测约束之后, 以后其他人修改这么代码导致 mutable 的时候, 也会 ci 报错的, 保证这个方法的长治久安
除此之外, 开启 Eslint 工具, 也可以对 Object 的 mutable 自动告警
3. 解构和 spread 语法
解构是 spread 预发是 ES6 中的新语法, 也是我个人用的非常多的, 一般代码的 immutable 写法, 都可以满足
Object 解构与 spread
比如我们有一个用户信息 A(几十项信息), 然后需要创建一个用户信息 userB, 除 idname 不一样之外, 其他都和 userA 一致
- const userA = {
- id: 1,
- name: 'hustcc',
- addr: 'Hangzhou',
- birth: '1992-08-01',
- // ...
- // 很多的数据不列出了
- };
- // 结构和 spread 写法创建 userB
- const userB = {
- ...userA,
- id: 2,
- name: 'ProtoTeam',
- };
数组 immutable 操作
- const arr = [1, 2, 3, 4, 5];
- // push 操作
- const arrPush = [
- ...arr,
- 6,
- ];
- // shift 操作
- const [e, ...arrShift] = arr;
- // concat
- const arrConcat = [...arr, ...arr];
四最后
在 react + redux 技术栈下, 不可变数据结构是大家都提倡的做法, 所以又很多框架层面的东西帮我们处理了这些事情
但是实际上, 我们在平时的业务代码开发中, 就需要有 immutable 的意识, 善用 ES6 语法, 就可以解决我们大部分的场景
对我们团队感兴趣的可以关注专栏, 关注 github 或者发送简历至'tao.qit####alibaba-inc.com'.replace('####', '@'), 欢迎有志之士加入~
来源: https://juejin.im/post/5aa8ae316fb9a028bd4c0202