Immutable.js简介

2023-09-19 07:30:00

引子

看一段大家熟悉的代码

const state = {
  str: 'wwming',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state

console.log(newState === state) // true

newState和state是相等的

原因: 由于js的对象和数组都是引用类型。所以newState的state实际上是指向于同一块内存地址的, 所以结果是newState和state是相等的。

尝试修改一下数据

const state = {
  str: 'wwming',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = state

newState.str = 'wwming is cool'

console.log(state.str, newState.str) // wwming is cool wwming is cool

可以看到,newState的修改也会引起state的修改。

如何解决?

js中提供了另一种修改数据的方式,要修改一个数据之前先制作一份数据的拷贝,像这样

const state = {
  str: 'wwming',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)

newState.str = 'wwming is cool'

console.log(state.str, newState.str)

可以使用很多方式在js中复制数据,比如:

  • Object.assign,
  • Object.freeze,
  • slice,
  • concat,
  • map,
  • filter,
  • reduce

等方式进行复制,但这些都是浅拷贝,就是只拷贝第一层数据。

更深层的数据还是同一个引用,比如:

const state = {
  str: 'wwming',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = Object.assign({}, state)

newState.obj.y = 2
newState.arr.push(4) 
console.log(state, newState) 

// 执行结果

{
    "str": "wwming",
    "obj": {
        "y": 2
    },
    "arr": [
        1,
        2,
        3,
        4
    ]
}

{
    "str": "wwming",
    "obj": {
        "y": 2
    },
    "arr": [
        1,
        2,
        3,
        4
    ]
}

可以看到,当在更改newState更深层次的数据的时候,还是会影响到state的值。

如果要深层复制,就得一层一层的做 递归拷贝,这是一个复杂的问题。

虽然有些第三方的库已经帮我们做好了,比如lodash的cloneDeep方法。深拷贝是非常消耗性能的

import { cloneDeep } from 'lodash'

const state = {
  str: 'wwming',
  obj: {
    y: 1
  },
  arr: [1, 2, 3]
}
const newState = cloneDeep(state)

newState.obj.y = 2
newState.arr.push(4)

console.log(state, newState) 

解决这个问题,就引出了 不可变数据(Immutable Data?)

什么是不可变数据 (Immutable Data)?

Immutable data encourages pure functions (data-in, data-out) and lends itself to much simpler application development and enabling techniques from functional programming such as lazy evaluation.

– 官方文档对其描述

Immutable Data 就是 一旦创建,就不能再被更改的数据

对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。

Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。

同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

更多精彩内容,请微信搜索“前端爱好者戳我 查看

immutable.js的优缺点

优点:
  • 降低mutable带来的复杂度
  • 节省内存
  • 历史追溯性(时间旅行):时间旅行指的是,每时每刻的值都被保留了,想回退到哪一步只要简单的将数据取出就行,想一下如果现在页面有个撤销的操作,撤销前的数据被保留了,只需要取出就行,这个特性在redux或者flux中特别有用
  • 拥抱函数式编程:immutable本来就是函数式编程的概念,纯函数式编程的特点就是,只要输入一致,输出必然一致,相比于面向对象,这样开发组件和调试更方便。推荐一本函数式编程的在线免费书《JS 函数式编程指南》, 此书可以推荐给学生做为课外补充阅读。
缺点:
  • 需要重新学习api
  • 资源包大小增加(源码5000行左右)
  • 容易与原生对象混淆:由于api与原生不同,混用的话容易出错。

----------------------- 优点 -------------------

降低mutable带来的复杂度

共享的可变状态是万恶之源,举个简单的例子就是js中的引用赋值:

var obj = { a: 1 };
var copy_obj = obj;
copy_obj.a = 2;
console.log(obj.a); // 2

引用赋值虽然可以节省内存,但当应用复杂之后,可变状态往往会变成噩梦.

通常一般的做法是使用shallowCopy或者deepCopy来避免被修改,但这样造成了CPU和内存的消耗.

Immulate可以很好地解决这些问题。

节省内存空间

上面提到了结构共享,Immutable.js 使用这种方式会尽量复用内存,甚至以前使用的对象也可以再次被复用。

没有被引用的对象会被垃圾回收。

import { Map } from 'immutable';

let a = Map({
  select: 'users',
  filter: Map({ name: 'Cam' })
})

let b = a.set('select', 'people');

a === b; // false
a.get('filter') === b.get('filter'); // true

上面 a 和 b 共享了没有变化的 filter 节点。

Undo/Redo,Copy/Paste,随意穿越!

因为每次数据都是不一样的,只要把这些数据放到一个数组里储存起来,想回退到哪里就拿出对应数据即可,很容易开发出撤销重做这种功能。

拥抱函数式编程

Immutable(持久化数据结构)本身就是函数式编程中的概念。

函数式编程关心数据的映射,命令式编程关心解决问题的步骤,纯函数式编程比面向对象更适用于前端开发。

因为只要输入一致,输出必然一致,这样开发的组件更易于调试和组装。

----------------------- 缺点 -------------------

需要重新学习api
资源包大小增加(源码5000行左右)
容易与原生对象混淆:由于api与原生不同,混用的话容易出错

主要是Immutable的API设计的和原生对象类似,容易混淆操作。

例如其中Map和List的操作:

// Immutable
const map = Map({ a: 1, b: 2 });
const list = List([1,2,3]);
// 原生js
const obj = { a: 1, b: 2 };
const arry = [1,2,3];
// 取值方式对比
console.log(map.get('a'));
console.log(list.get(0));

console.log(obj.a);
console.log(arry[0]);

参考文档:

  • https://blog.csdn.net/weixin_44216510/article/details/118073411
  • https://zhuanlan.zhihu.com/p/101534155
更多推荐

文件内容显示

一.浏览普通文件.1.文件内容查看1.1.1.cat命令作用:查看文件内容,适合数据量较少格式:cat-参数文件名参数:-n:显示行号,加上-b:文件中所有非空行增加行号,编号从1开始例:查看主机域名映射文件:[root@server~]#cat/etc/hosts127.0.0.1为回环地址::1为IPV6格式扩展:

【2023,学点儿新Java-48】变量与运算符 (阶段性复习):关键字和保留,回顾:标识符的命名规则,变量的基本使用

前情提要:【2023,学点儿新Java-47】常见字符集介绍:ASCII码、ISO-8859-1字符集、GBxxx字符集、Unicode码的缺陷、UTF-8|补充:条件运算符的练习【2023,学点儿新Java-46】条件运算符:语法格式及示例;基础练习:获取两个数/三个数中的较大值;星期运算|附:测试代码位运算符的使用

【SLAM】 前端-视觉里程计之特征点

前端-视觉里程计之特征点参考资料:以不变应万变:前端-视觉里程计之特征点视觉SLAM——特征点法task05本次了解了特征点是由关键子和描述子组成,并且对比了SIFT、SURF等七种获取特征点的方法,同时对比了SIFT、SURF和ORB方法之间的优劣,如果需要尺度和旋转不变性,SIFT和SURF可能更合适,但如果需要实

Docker自定义镜像

一、镜像结构镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。镜像是分层结构,每一层称为一个LayerBaseImage层:包含基本的系统函数库、环境变量、文件系统其它:在BaseImage基础上添加依赖、安装程序、完成整个应用的安装和配置Entrypoint:入口,是镜像中应用启动的命令二、什么是Doc

JVM面试题-JVM内存结构解析(图文详解)

JVM内存结构共享和隔离线程共享区域:方法区、堆、直接内存线程隔离区域:虚拟机栈、本地方法栈、程序计数器线程共享:定义一个变量或者一个方法,多线程都可以同时访问、修改这个方法或者变量线程隔离:就是数据不能被多个线程同时访问,某些数据只属于一个线程1.程序计数器线程私有的。作用:记录线程执行到哪一步,保存的是字节码的行号

第九章 常用服务器的搭建

第九章常用服务器的搭建1.配置FTP服务器1.1.FTP简介​FTP(FileTransferProtocol,文件传送协议)是TCP/IP网络上两台计算机间传送文件的协议,FTP是在TCP/IP网络和Internet上最早使用的协议之一,它属于网络协议组的应用层。FTP客户机可以给服务器发出命令来下载文件、上传文件、

在绘制业务流程图的时候方框的颜色选择上如何选择能提高辨识度,更加易于理解和记忆

在绘制业务流程图的时候方框的颜色选择上如何选择能提高辨识度,更加易于理解和记忆在绘制业务流程图时,方框的颜色选择是一种重要的视觉策略,可以帮助理解和记忆。以下是一些关于颜色选择的建议:使用对比色:对比色可以帮助人们更清楚地看到不同的元素,因此可以考虑使用对比色来区分不同的步骤或阶段。常规和异常流程区分:常规流程可以使用

2023工博会强势回归!智微工业携八大系列重磅亮相

中国国际工业博览会(简称"中国工博会")自1999年创办以来,历经二十余年发展创新,通过专业化、市场化、国际化、品牌化运作,已发展成为通过国际展览业协会(UFI)认证、中国工业领域规模最大、功能最全、水平最高、影响力最强的展览盛会之一。三年磨一剑,此次工博会众盼回归,智微工业借此以磅礴气势重构“智能工业新定义”,携八大

第十届国家网络安全宣传周今日在全国范围内启动

十届国家网络安宣传周回望2023年国家网络安全宣传周于9月11日至17日在全国范围内统一开展。其中,开幕式等重要活动在福建省福州市举行。第一届国家网络安全宣传周活动始于2014年,此后将每年9月的第三周定为活动举行日。网络安全宣传周至今已经走过10个年头。作为网络安全界的盛会,每一年都是嘉宾云集,大咖齐聚,展示前言技术

MySQL索引、事务、事务与存储引擎

1、索引1.1索引的概念●索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址)。●使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的物理地址然后访问相应的数据,因此能加快数据库的查询速度。●索引就好比是一本书的

VLANIF配置

目录实验原理:案例:设备配置用ping验证不同vlan之间实现相互通信实验原理:VLANIF接口是一种第三层的逻辑接口,用于在第三层实现不同VLAN之间的通信。每个VALN有一个VLANIF接口,并通过该接口在网络层转发VLAN通信。由于每个VLAN是一个广播域,每个VLAN可以被看作是一个IP网段,因此可以把VLAN

热文推荐