1 引言
读完文章才发现,文章标题改为 Redux 的多态性更妥当,因为整篇文章都在说 Redux,而 Redux 使用场景不局限于 React。
2 概述
先看看普通的 redux 的 reducer:
const todo = (state = {}, action) => {
switch (action.type) {
case "ADD_TODO":
return {
id: action.id,
text: action.text,
completed: false
};
case "TOGGLE_TODO":
if (state.id !== action.id) {
return state;
}
return Object.assign({}, state, {
completed:
});
default:
return state;
}
};
我们简化一下使用场景,假设基于这个 reducer todo
,生成了两个新 store s1
s2
:
const s1 = todo(
{},
{
type: "ADD_TODO",
id: 1,
text: "Finish blog post"
}
);
const s2 = todo(s1, {
type: "TOGGLE_TODO",
id: 1
});
至于为什么会阻止 js 引擎的 shapes 优化,看下面的代码:
// transition-trees.js
let a = {x:1, y:2, z:3};
let b = {};
b.x = 1;
b.y = 2;
b.z = 3;
console.log("a is", a);
console.log("b is", b);
console.log("a and b have same map:", %HaveSameMap(a, b));
通过 node --allow-natives-syntax test.js
执行,通过调用 node 原生函数 %HaveSameMap
判断这种情况下 a
与 b
是否共享一个 shape(v8 引擎的 Shape 实现称为 Map)。
结果是 false
,也就是 js 引擎无法对 a
b
做 Shapes 优化,这是因为 a
与 b
对象初始化的方式不同。
同样,在 Redux 代码中常用的 Object.assign
也有这个问题:
因为新的对象以 {}
空对象作为最初状态,js 引擎会为新对象创建 Empty Shape,这与原对象的 Shape 一定不同。
顺带一提 es6 的解构语法也存在同样的问题,因为 babel
将解构最终解析为 Object.assign
:
对这种尴尬的情况,作者的建议是对所有对象赋值时都是用 Object.assign
以保证 js 引擎可以做 Shapes 优化:
let a = Object.assign({}, {x:1, y:2, z:3});
let b = Object.assign({}, a);
console.log("a is", a);
console.log("b is", b);
console.log("a and b have same map:", %HaveSameMap(a, b)); // true
3 精读
作者描述的性能问题是引擎级别的 Shapes 优化问题,读过上篇精读就很容易知道,只有相同初始化方式的对象才被 js 引擎做优化,而 Redux 频繁生成的 immutable 全局 store 是否能被优化呢?答案是“往往不能”,因为 immutable 赋值问题,我们往往采用 Object.assign
或者解构方式赋值,这种方式产生的新对象与原对象的 Shape 不同,导致 Shape 无法复用。
这里解释一下疑惑,为什么说 immutable 对象之间也要优化呢?这不是两个不同的引用吗?这是因为 js 引擎级别的 Shapes 优化就是针对不同引用的对象,将对象的结构:Shape 与数据分离开,这样可以大幅优化存储效率,对数组也一样,上一篇精读有详细介绍。
4 总结
原文提到的多态是指多个相同结构对象,被拆分成了多个 Shape;而单态是指这些对象可以被一个 Shape 复用。
笔者以前也经历过从 Object.assign
到 Immutablejs 库,最后又回到解构新语法的经历,觉得在层级不深情况下解构语法可以代替 Immutablejs 库。
通过最近两篇精读的分析,我们需要重新思考这样做带来的优缺点,因为在 js 环境中,Object.assign
的优化效率比 Immutablejs 库更低。
最后,也完全没必要现在就开始重构,因为这只是 js 运行环境中很小一部分影响因素,比如为了引入 Immutablejs 让你的网络延时增加了 100%?所以仅在有必要的时候优化它。