本周精读的文章是:surprising-polymorphism-in-react-applications,看看做者是如何解释这个多态性含义的。javascript
读完文章才发现,文章标题改成 Redux 的多态性更稳当,由于整篇文章都在说 Redux,而 Redux 使用场景不局限于 React。java
Redux immutable 特性可能产生浏览器没法优化的性能问题,也就是浏览器没法作 shapes 优化,也就是上一篇精读《JS 引擎基础之 Shapes and Inline Caches》 里提到的。node
先看看普通的 redux 的 reducer:react
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: !state.completed }); default: return state; } };
咱们简化一下使用场景,假设基于这个 reducer todo
,生成了两个新 store s1
s2
:git
const s1 = todo( {}, { type: "ADD_TODO", id: 1, text: "Finish blog post" } ); const s2 = todo(s1, { type: "TOGGLE_TODO", id: 1 });
看上去很常见,也的确如此,咱们每次 dispatch 都会根据 reducer 生成新的 store 树,并且是一个新的对象。然而对 js 引擎而言,这样的代码可能作不了 Shapes 优化(关于 Shapes 优化建议阅读上一期精读 Shapes 优化),也就是最须要作优化的全局 store,在生成新 store 时没法被浏览器优化,这个问题很容易被忽视,但的确影响不小。es6
至于为何会阻止 js 引擎的 shapes 优化,看下面的代码:github
// 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)。typescript
结果是 false
,也就是 js 引擎没法对 a
b
作 Shapes 优化,这是由于 a
与 b
对象初始化的方式不一样。redux
一样,在 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
这篇文章须要与上一篇 精读《JS 引擎基础之 Shapes and Inline Caches》 连起来看容易理解。
做者描述的性能问题是引擎级别的 Shapes 优化问题,读过上篇精读就很容易知道,只有相同初始化方式的对象才被 js 引擎作优化,而 Redux 频繁生成的 immutable 全局 store 是否能被优化呢?答案是“每每不能”,由于 immutable 赋值问题,咱们每每采用 Object.assign
或者解构方式赋值,这种方式产生的新对象与原对象的 Shape 不一样,致使 Shape 没法复用。
这里解释一下疑惑,为何说 immutable 对象之间也要优化呢?这不是两个不一样的引用吗?这是由于 js 引擎级别的 Shapes 优化就是针对不一样引用的对象,将对象的结构:Shape 与数据分离开,这样能够大幅优化存储效率,对数组也同样,上一篇精读有详细介绍。
因此笔者更推荐使用好比 immutable-js 这种库操做 immutable 对象,而不是 Object.assign,由于封装库内部是可能经过统一对象初始化方式利用 js 引擎进行优化的。
原文提到的多态是指多个相同结构对象,被拆分红了多个 Shape;而单态是指这些对象能够被一个 Shape 复用。
笔者之前也经历过从 Object.assign
到 Immutablejs 库,最后又回到解构新语法的经历,以为在层级不深状况下解构语法能够代替 Immutablejs 库。
经过最近两篇精读的分析,咱们须要从新思考这样作带来的优缺点,由于在 js 环境中,Object.assign
的优化效率比 Immutablejs 库更低。
最后,也彻底不必如今就开始重构,由于这只是 js 运行环境中很小一部分影响因素,好比为了引入 Immutablejs 让你的网络延时增长了 100%?因此仅在有必要的时候优化它。
讨论地址是: 精读《React 的多态性》 · Issue #92 · dt-fe/weekly
若是你想参与讨论,请点击这里,每周都有新的主题,周末或周一发布。