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 相关文章推荐
qTip 基于JQuery的Tooltip插件[兼容性好]
Sep 01 Javascript
jquery三个关闭弹出层的小示例
Nov 05 Javascript
Jquery实现兼容各大浏览器的Enter回车切换输入焦点的方法
Sep 01 Javascript
jQuery插件 Jqplot图表实例
Jun 18 Javascript
jQuery窗口拖动功能的实现代码
Feb 04 Javascript
Angular1.x复杂指令实例详解
Mar 01 Javascript
Babel 入门教程学习笔记
Jun 13 Javascript
详解webpack+ES6+Sass搭建多页面应用
Nov 05 Javascript
微信小程序开发(二):页面跳转并传参操作示例
Jun 01 Javascript
html+vue.js 实现漂亮分页功能可兼容IE
Nov 07 Javascript
原生JavaScript实现进度条
Feb 19 Javascript
JS新手入门数组处理的实用方法汇总
Apr 07 Javascript
基于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 rawurlencode与urlencode函数的深入分析
2013/06/08 PHP
php禁止直接从浏览器输入地址访问.php文件的方法
2014/11/04 PHP
深入理解PHP中的Streams工具
2015/07/03 PHP
PHP后台微信支付和支付宝支付开发
2017/04/28 PHP
PHP实现的杨辉三角求解算法分析
2019/03/11 PHP
PHP封装类似thinkphp连贯操作数据库Db类与简单应用示例
2019/05/08 PHP
laravel 实现向公共模板中传值 (view composer)
2019/10/22 PHP
javascript 避免闭包引发的问题
2009/03/17 Javascript
jQuery弹出(alert)select选择的值
2013/04/21 Javascript
jQuery实现的图片分组切换焦点图插件
2015/01/06 Javascript
jQuery实现仿微软首页感应鼠标变化滑动窗口效果
2015/10/08 Javascript
Bootstrap项目实战之首页内容介绍(全)
2016/04/25 Javascript
jQuery remove()过滤被删除的元素(推荐)
2017/07/18 jQuery
js学习总结之DOM2兼容处理顺序问题的解决方法
2017/07/27 Javascript
JS设计模式之单例模式(一)
2017/09/29 Javascript
详解React Native 采用Fetch方式发送跨域POST请求
2017/11/15 Javascript
利用JS动态生成隔行换色HTML表格的两种方法
2018/10/09 Javascript
微信小程序tabBar设置实例解析
2019/11/14 Javascript
剖析Django中模版标签的解析与参数传递
2015/07/21 Python
简单解决Python文件中文编码问题
2015/11/22 Python
使用python装饰器计算函数运行时间的实例
2018/04/21 Python
解决新版Pycharm中Matplotlib图像不在弹出独立的显示窗口问题
2019/01/15 Python
python使用MQTT给硬件传输图片的实现方法
2019/05/05 Python
使用Html5中的cavas画一面国旗
2019/09/25 HTML / CSS
在购买印度民族服饰:Soch
2020/09/15 全球购物
路由表示做什么用的?在linux环境中怎么来配置一条默认路由?
2013/06/07 面试题
毕业生找工作的自我评价
2013/10/18 职场文书
公司业务员岗位职责
2014/03/18 职场文书
爱情寄语大全
2014/04/09 职场文书
在职员工证明书
2014/09/19 职场文书
2014年新教师工作总结
2014/11/08 职场文书
2015年班组长工作总结
2015/04/10 职场文书
企业党员岗位承诺书
2015/04/27 职场文书
事业单位工作人员2015年度思想工作总结
2015/10/15 职场文书
Django实现在线无水印抖音视频下载(附源码及地址)
2021/05/06 Python
浅谈Python numpy创建空数组的问题
2021/05/25 Python