Immutable 在 JavaScript 中的应用


Posted in Javascript onMay 02, 2016

Mutable 对象
在 JavaScript 中,对象是引用类型的数据,其优点在于频繁的修改对象时都是在原对象的基础上修改,并不需要重新创建,这样可以有效的利用内存,不会造成内存空间的浪费,对象的这种特性可以称之为 Mutable,中文的字面意思是「可变」。
对于 Mutable 的对象,其灵活多变的优点有时可能会成为其缺点,越是灵活多变的数据越是不好控制,对于一个复杂结构的对象来说,一不小心就在某个不经意间修改了数据,假如该对象又在多个作用域中用到,此时很难预见到数据是否改变以及何时改变的。

var obj = { /* 一个复杂结构的对象 */ };
doSomething(obj);
// 上面的函数之行完后,此时的 obj 还是最初的那个 obj 吗?

针对这种问题,常规的解决办法可以通过将对象进行深拷贝的形式复制出一个新的对象,再在新对象上做修改的操作,这样能确保数据的可控性,但是频繁的复制会造成内存空间的大量浪费。

var obj = { /* 一个复杂结构的对象 */ };
// copy 出一个新的 obj2
// 但是 copy 操作会浪费内存空间
var obj2 = deepClone(obj);
doSomething(obj2);
// 上面的函数之行完后,无论 obj2 是否变化,obj 肯定还是原来那个 obj

Immutable 对象
为了能更好的解决上述的问题,出现了 Immutable 对象,Immutable 从字面上翻译成中文是「不可变」。每次修改一个 Immutable 对象时都会创建一个新的不可变的对象,在新对象上操作并不会影响到原对象的数据。这种特殊的对象并不是 JavaScript 新出的功能特性,而是业界为了解决这种问题提供的一套解决方案,并且涌现出了一些优秀的开源类库,其中最有名的就是 Facebook 的 Lee Byron 开源的 immutable.js。当然,Immutable 的这种解决方案并不是独创的,而是来源于 Clojure 和 Scala。
Mutable 和 Immutable 的性能对比
对于 Mutable 的对象的低效率操作主要体现在复制和比较上,而 Immutable 对象就是解决了这两大低效的痛点。
普通的 Mutable 对象的深拷贝操作会将一整份数据都复制一遍,而 Immutable 对象在修改数据时并不会复制一整份数据,而是将变化的节点与未变化的节点的父子关系转移到一个新节点上,类似于链表的结构。从 “复制” 的角度来看,做到了最小化的复制,未变化的部分都是共享的,Mutable 在复制的时候是 “全量”,而 Immutable 复制的是 “增量”,对于内存空间的使用率的比较高低立判。
并且基于每次修改一个 Immutable 对象都会创建一个新的 Immutable 对象的这种特性可以将数据的修改状态保存成一组快照,这也是挺方便的。
再来说说比较操作。对于 Mutable 的对象,如果要比较两个对象是否相等,必须遍历对象的每个节点进行比较,对于结构复杂的对象来说,其效率肯定高不到哪去。对于 Immutable 对象,immutable.js 提供了直接判断两个 Immutable 对象的「值」是否相等的 API。

var map1 = Immutable.Map({a:1, b:1, c:1});
var map2 = Immutable.Map({a:1, b:1, c:1});
assert(map1 !== map2); // 不同的 Immutable 实例,此时比较的是引用地址
assert(Immutable.is(map1, map2)); // map1 和 map2 的值相等,比较的是值
assert(map1.equals(map2)); // 与 Immutable.is 的作用一样

在实际的开发应用中,性能并不总是最关键和重要的,对于普通的 JavaScript 的项目来说,由于 Immutable 的特性带来的数据的可控性比起性能来说更有优势,对于 Mutable 对象适合在封闭的作用域小范围使用,而 Immutable 对象适合数据需要跨多个作用域传递时使用。

Mutable 和 Immutable 在使用上的区别

immutable.js 提供了多种 Immutable 的数据结构:包含了 List Stack Map OrderedMap Set OrderedSet Record,这些数据结构与原生的 Mutable 的数据结构大致对应。
各数据结构的用法这里不细说,主要说说 Immutable 对象与 Mutable 对象在使用上的区别吧。
原生的 Mutable 对象在「读」和「写」上非常方便。

var mutableObj = {};
// 写入数据
mutableObj.foo = 'bar';
// 读取数据
console.log(mutableObj.foo);

而 Immutable 对象需要通过 set 和 get 来对数据进行「读」和「写」。

var immutableObj1 = Immutable.Map();
// 写入数据
var immutableObj2 = immutableObj1.set('foo', 'bar');
// 读取数据
console.log(immutableObj2.get('foo')); // => 'bar'

上面的例子为了说明 set 方法的使用才在一开始创建了一个空对象,实际上可以在实例化的时候传初始值。

var immutableObj = Immutable.Map({'foo', 'bar'});

对于层级比较深的数据,immutable.js 提供的访问接口很方便。

var immutableObj1 = Immutable.fromJS({
 a: {
  b: 'c'
 },
 d: [1, 2, 3]
});
// 读取深层级的数据
console.log(immutableObj1.getIn(['a', 'b'])); // => 'c'
console.log(immutableObj1.getIn(['d', 1])); // => 2
// 修改深层级的数据
var immutableObj2 = immutableObj1.setIn(['a', 'b'], 'd');
console.log(immutableObj2.getIn(['a', 'b'])); // => 'd'

如果是原生的 Mutable 对象,在链式访问一个深层级的数据时可能会报对象 undefined 的错误,而 Immutable 对象在碰到这种情况时不会报错,返回的是 undefined。
在调试的时候,如果想查看一个 Immutable 对象的内部结构,建议使用 toJSON() 先转换为普通的 Mutable 对象。

Javascript 相关文章推荐
Javascript处理DOM元素事件实现代码
May 23 Javascript
JavaScript包装对象使用介绍
Aug 29 Javascript
使用node.js 制作网站前台后台
Nov 13 Javascript
JS烟花背景效果实现方法
Mar 03 Javascript
浅析jquery unbind()方法移除元素绑定的事件
May 24 Javascript
原生javascript 学习之js变量全面了解
Jul 14 Javascript
Angular实现一个简单的多选复选框的弹出框指令实例
Apr 25 Javascript
用Node编写RESTful API接口的示例代码
Jul 04 Javascript
Vue监听页面刷新和关闭功能
Jun 20 Javascript
Vue.js自定义指令学习使用详解
Oct 19 Javascript
Javascript表单序列化原理及实现代码详解
Oct 30 Javascript
一篇文章告诉你如何实现Vue前端分页和后端分页
Feb 18 Vue.js
基于javascript数组实现图片轮播
May 02 #Javascript
Javascript HTML5 Canvas实现的一个画板
Apr 12 #Javascript
bootstrap3 兼容IE8浏览器!
May 02 #Javascript
javascript实现列表切换效果
May 02 #Javascript
基于javascript编写简单日历
May 02 #Javascript
Bootstrap媒体对象的实现
May 01 #Javascript
Bootstrap进度条组件知识详解
May 01 #Javascript
You might like
php 获取可变函数参数的函数
2009/08/26 PHP
关于查看MSSQL 数据库 用户每个表 占用的空间大小
2013/06/21 PHP
PHP中执行MYSQL事务解决数据写入不完整等情况
2014/01/07 PHP
PHP6 中可能会出现的新特性预览
2014/04/04 PHP
PHP实现一个简单url路由功能实例
2016/11/05 PHP
用js实现预览待上传的本地图片
2007/03/15 Javascript
js prototype 格式化数字 By shawl.qiu
2007/04/02 Javascript
jQuery.getScript加载同域JS的代码
2012/02/13 Javascript
JavaScript数组各种常见用法实例分析
2015/08/04 Javascript
jQuery实现form表单元素序列化为json对象的方法
2015/12/09 Javascript
jquery中实现时间戳与日期相互转换
2016/04/12 Javascript
浅谈js的ajax的异步和同步请求的问题
2016/10/07 Javascript
深入理解JS中的Function.prototype.bind()方法
2016/10/11 Javascript
JS简单实现滑动加载数据的方法示例
2017/10/18 Javascript
在vue项目中安装使用Mint-UI的方法
2017/12/27 Javascript
vue+element实现表格新增、编辑、删除功能
2019/05/28 Javascript
微信小程序上传图片并等比列压缩到指定大小的实例代码
2019/10/24 Javascript
[02:11]完美世界DOTA2联赛10月28日赛事精彩集锦:来吧展示实力强劲
2020/10/29 DOTA
[01:01:41]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Magma BO3 第二场 1月31日
2021/03/11 DOTA
Python中使用语句导入模块或包的机制研究
2015/03/30 Python
在Python中调用ggplot的三种方法
2015/04/08 Python
python基于pyDes库实现des加密的方法
2017/04/29 Python
Python处理命令行参数模块optpars用法实例分析
2018/05/31 Python
在mac下查找python包存放路径site-packages的实现方法
2018/11/06 Python
django项目中使用手机号登录的实例代码
2019/08/15 Python
浅谈keras中的目标函数和优化函数MSE用法
2020/06/10 Python
python 如何调用 dubbo 接口
2020/09/24 Python
泰国时尚电商:POMELO Fashion
2020/03/11 全球购物
派出所所长先进事迹
2014/05/19 职场文书
2014年个人技术工作总结
2014/12/08 职场文书
小学生运动会广播
2015/08/19 职场文书
教师素质教育心得体会
2016/01/19 职场文书
浅谈Python中对象是如何被调用的
2022/04/06 Python
如何通过简单的代码描述Angular父组件、子组件传值
2022/04/07 Javascript
详细介绍python操作RabbitMq
2022/04/12 Python
Nginx 502 bad gateway错误解决的九种方案及原因
2022/08/14 Servers