您所在的位置:首页 / 行业动态

处理 JavaScript 复杂对象:深拷贝、Immutable & Immer

2018.10.11

9343

男神苟哥

我们知道 js 对象是按共享传递(call by sharing)的,因此在处理复杂 js 对象的时候,往往会因为修改了对象而产生副作用

深拷贝

如果需要深拷贝,拷贝的时候判断一下属性值的类型,如果是对象,再递归调用深拷贝函数即可,具体实现可以参考 jQuery 的$.extend。实际上需要处理的逻辑分支比较多,在 lodash 中 的深拷贝函数 cloneDeep 甚至有上百行,那有没有简单粗暴点的办法呢?

JSON.parse

最原始又有效的做法便是利用JSON.parse将该对象转换为其 JSON 字符串表示形式,然后将其解析回对象:

const deepClone(obj) => JSON.parse(JSON.stringify(obj)); 复制代码

对于大部分场景来说,除了解析字符串略耗性能外(其实真的可以忽略不计),确实是个实用的方法。但是尴尬的是它不能处理循环对象(父子节点互相引用)的情况,而且也无法处理对象中有 function、正则等情况。

MessageChannel

MessageChannel 接口是信道通信 API 的一个接口,它允许我们创建一个新的信道并通过信道的两个 MessagePort 属性来传递数据

利用这个特性,我们可以创建一个MessageChannel,向其中一个 port 发送数据,另一个 port 就能收到数据了。

function structuralClone(obj) { return new Promise(resolve => { const {port1, port2} = new MessageChannel();
            port2.onmessage = ev => resolve(ev.data);
            port1.postMessage(obj);
        });
    } const obj = /* ... */ const clone = await structuralClone(obj); 复制代码

除了这样的写法是异步的以外也没什么大的问题了,它能很好的支持循环对象、内置对象(Date、 正则)等情况,浏览器兼容性也还行。但是它同样也无法处理对象中有 function的情况。

类似的 API 还有History API、Notification API等,都是利用了结构化克隆算法(Structured Clone) 实现传输值的。

Immutable

如果需要频繁地操作一个复杂对象,每次都完全深拷贝一次的话效率太低了。大部分场景下都只是更新了这个对象一两个字段,其他的字段都不变,对这些不变的字段的拷贝明显是多余的。看看 Dan Abramov 大佬说的:

Dan Abramov)

这些库的关键思路即是:创建持久化的数据结构(Persistent data structure),在操作对象的时候只 clone 变化的节点和其祖先节点,其他的保持不变,实现结构共享(structural sharing)。例如在下图中红色节点发生变化后,只会重新产生绿色的 3 个节点,其余的节点保持复用(类似软链的感觉)。这样就由原本深拷贝需要创建的 8 个新节点减少到只需要 3 个新节点了。

结构共享

Immutable.js

在Immutable.js中这里的 “节点” 并不能简单理解成对象中的 “key”,其内部使用了Trie(字典树)数据结构,Immutable.js会把对象所有的 key 进行 hash 映射,将得到的 hash 值转化为二进制,从后向前每 5 位进行分割后再转化为 Trie 树。

举个例子,假如有一对象 zoo:

zoo={ 'frog':

相关新闻

“成都社保”微信公众平台上线

2016.07.18

3485

“成都社保”微信公众平台正式上线运行,是“互联网+社保”行动的又一创新成果,标志着成都社保经办管理服务现代治理体系建设迈出坚实步伐。

浅聊在 uni-app 中如何选择一个合适的 UI 组件库

2021.09.23

2952

uni-app 框架转眼已经发布了一年多,使用 uni-app 的开发者也是与日俱增。

再使用“123456”密码就“违法”了!!!

2018.10.16

8897

据外媒 Techcrunch 报道,近日,美国加州通过了一项法律,该法律规定,在 2020 年之后,新的电子产品不允许再使用“admin”、“123456”和经典的“password”这样简单的默认密码