react系列(16)不可突变的数据结构

本文深入探讨了Immutable.js库在React开发中的应用,解释了其如何通过持久化数据结构和结构共享,提供高效且安全的数据管理解决方案,避免了深拷贝带来的资源浪费。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JS中的对象一般是可变的(Mutable),因为使用了引用赋值,新的对象简单的引用了原始对象,改变新的对象将影响到原始对象。如 foo={a: 1}; bar=foo; bar.a=2 你会发现此时 foo.a 也被改成了 2。虽然这样做可以节约内存,但当应用复杂后,这就造成了非常大的隐患,突变带来的优点变得得不偿失。为了解决这个问题,一般的做法是使用 shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但缺点是造成了 CPU 和内存的浪费。

如深拷贝的代码如下:

var cloneDeep = require('lodash.clonedeep'); // npm install lodash.clonedeep

var data = {
	id: '1',
	author: {
		name: 'Lee'
	}
};

var dataDeepCopy = cloneDeep(data);

console.log(dataDeepCopy === data); // 打印 false

dataDeepCopy.id = '2';
dataDeepCopy.author.name = 'Huang';

console.log(data.id); // 打印 1  
console.log(dataDeepCopy.id); // 打印 2

 

什么是 IMMUTABLE DATA?

Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改操作都会返回一个新的 Immutable 对象。Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且没有发生变化。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

react目前最常用的immutable的方法是Immutable.fromJS() 和 Immutable.Record(),本篇主要通过这两种用法来认识immutable。

 

Immutable.fromJS(Object) 入参数据格式为对象

 这个函数的主要功能是将js的object对象格式{ }转换为Map格式,该Map是独一无二的,不可改变的。换种说法,等同于深度拷贝。什么是Map格式?它跟Java中的Map是一样的,数据表现形式为键值模式Map(key, value)。在同一个Map中每个key都是唯一的,通过key可以获取一个唯一的值。先看个简单的例子:

let student = Immutable.fromJS({
	id: 1,
	msg: {
		name: "John",
		age: 30
	},
	school: {
		sid: "1",
		sname: "Harvard"
	}
});
console.log(student.get('id')); // 打印 1
console.log(student.get('msg')); // 打印Map对象
console.log(student.get('msg').toJS()); // 打印 {name: "John", age: 30}

Map格式通过get(attr)来获取数据,这里要注意,当该属性如上例中的msg也为对象时,此时get获得的还是一个Map对象,Map对象可以通过toJS() 转换为 react可用的 Object对象格式。那么怎么去设置(或说深拷贝)一个新的Map对象呢?可以通过set设置:

let studentImmuCopy = student.set('id', 2).set('msg', Immutable.Map({
	name: "Lee",
	age: 29
}));
console.log(studentImmuCopy.get('id')); // 打印 2
console.log(studentImmuCopy.get('msg')); // 打印 Map对象
console.log(studentImmuCopy.get('msg').toJS()); // 打印 {name: "Lee", age: 29}
console.log(student.get('school') === studentImmuCopy.get('school')); // 打印 true

通过打印结果,你会发现student和 studentImmuCopy是两个完全不同的Map对象,studentImmuCopy 继承了student的数据结构并修改了部分数据,如id和msg,但是school并没有修改,所以在最后一个比较打印出来的结果是true,表示两者的school属性指向的是同一个内存位置。这正是Immutable的强大之处,深度拷贝,只改变了set的重新设置的值,没有被改变的值依然共享(指向)同一个内存位置。当set的值在原对象中没有该属性时,则新增这个属性值。

使用delete来删除某个属性值,如删除msg脚本如下:

console.log(student.delete('msg').toJS()); // 打印 {id:1, school:{sid:1,sname:"Harvard"}}

 

Immutable.fromJS(Array) 入参数据格式为数组

 这个函数的主要功能是将js的Array数组格式[ ]转换为List格式,与Map一样,得到的List 是独一无二的。请看下面用法:

let arr = Immutable.fromJS([1, 2, 3, 4, 5]);
console.log(arr); // List
console.log(arr.toJS()); // [1, 2, 3, 4, 5]
console.log(arr.size); // List 长度
console.log(arr.count()); // List 长度
console.log(arr.set(1, 0).toJS()); // 索引值从零开始计算,将索引值为1的值换为0, 打印[1, 0, 3, 4, 5]
console.log(arr.set(-1, 0).toJS()); // 将索引值为-1的值换为 0,此处为倒数第一个  [1, 2, 3, 4, 0]
console.log(arr.insert(1, 0).toJS()); // 将索引值为1的位置插入新的值0 [1, 0, 2, 3, 4, 5]
console.log(arr.push(6).toJS()); // 在列表最后插入数值为6的值
console.log(arr.delete(1).toJS()); // 删除索引值为1 的值 [1, 3, 4, 5]
console.log(arr.clear().toJS()); // 清空列表 []

List 数据类型也拥有 pop 、 push 、 shift 、 unshift 这四种操作方法,和原生 Array的四种方法使用方式一致,但唯一区别就是返回新的 List ,并且不改变原来的数组本身,而原生则是会改变元素本身。

 

Immutable.Record() 

Immutable.Record()指定义一个实例结构模板,通过new方法创建与该模板相同结构的实例。

// record 模板实例
const studentRecord = Immutable.Record({
	id: "",
	msg: {
		name: "",
		age:  ""
	},
	school: {
		sid: "1",
		sname: "Harvard"
	}
});
const x = new studentRecord({
	id: '1',
	msg: {
		name: "Lee",
		age: "29"
	}
});

const y = x.set('id', '2').set('msg', {
	name: 'Huang',
	age: '30'
});
console.log(x); // 打印 Record 对象
console.log(x.id); // 打印 1
console.log(x.msg); // {name: "Lee", age: "29"}
console.log(x.msg.name); // 打印 Lee
console.log(y.msg.name); // 打印 Huang
console.log(x.toJS()); // 打印 {id:'1',msg:{name:'Lee',age:"29"},school:{sid:"1",sname:"Harvard"}}
console.log(y.toJS()); // 打印 {id:'2',msg:{name:'Huang',age:"30"},school:{sid:"1",sname:"Harvard"}}
console.log(x.scholl === y.scholl); // 打印 true

Record对象与Map对象不同,无需通过get()来获取属性。

总的来说,在react开发,当需要用到深度拷贝时,可以使用Immutable来替代 require('lodash.clonedeep'),当然Immutable 还有很多很强大的功能,可以到官网查看哦。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值