持而盈之,不如其已;揣而锐之,不可长保。金玉满堂,莫之能守;富贵而骄,自遗其咎。
Rust 中的不可变字符串
fn main() {
println!("Hello, world!");
let a = "this is string";
println!("{}", a);
}
在 rust 中, a 开始了赋值,后面又重新赋值,rust 会报错的,报错的源影是:cannot assign twice to immutable variable。
fn main() {
println!("Hello, world!");
let mut a = "this is string";
println!("{}", a);
a = "new string"
println!("{}", a);
}
rust 很严格,如果我们在没有使用就是重新分配变量的话,会有提示
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
总结:在 rust 中将可变数据和非可变数据分特别的清新,但是在 JS 中并没有,因为 JS 产生就是为了追求灵活。可变性的极大的增加了 JS 的灵活性,但是在 Redux 的 reduce 中,却需要不可变数据和数据的持久化。
什么是不可变数据,以及数据类型与可变数据的关系
引用数据类型数据,因为有共同的指向,数据在一处改变,对其引用的数据都会发生改变。有时候我们不希望这样,我们希望拷贝一份不可变的数据。immutable.js 能很好的帮助我们,但是在这之前,我们也有一些方法来实现。
- Object.assign 深拷贝(第一层深拷贝)
let obj = { a: 1, b: 2 };
let assignObj = Object.assign(obj, { x: 123 });
obj === assignObj; // true
let cloneObj = Object.assign({}, obj, { x: 123 });
obj === assignObj; // false
- JSON.stringify 转变为 JSON 字符串,然后再解析为 JSON 对象。JSON 拷贝方法不适合函数
function JSONDeepClone(obj) {
let tmp = JSON.stringify(obj);
let result = JSON.parse(tmp);
return result;
}
- Object.create 方式
- 先获取原型属性
- 获取自己的属性
- 遍历属性
- 获取对象属性描述符
- 在原型对象上扩展属性
function deepCopy(obj) {
var copy = Object.create(Object.getPrototypeOf(obj));
var propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(name) {
var desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
}
- 最原始的是,递归遍历。
function deepClose(obj) {
// 初始化 考虑数组和对象两种情况
const objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (let key in obj) {
// 不考虑原型上的属性
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClose(obj[key]);
} else {
objClone[key] = deepClose(obj[key]);
}
}
}
}
}
Redux 中 reducer
我们在写 reducer 的时候,可能要特别的注意,我们不能直接返回 state, 必须返回一个 state 的副本。然后交给 store 的内容。我可能经常使用:
- 展开运算符
- Object.assign
- JSON
- ...
等方法来克隆一个新的对象完成任务,但是这个写都不够纯粹,于是 React 团队开发出了,持久数据的库 immutable.js。
理解
Immutable.js 采用了持久化数据结构和结构共享,保证每一个对象都是不可变的,任何添加、修改、删除等操作都会生成一个新的对象,且通过结构共享等方式大幅提高性能。
持久化
通俗点解释就是,对于一个持久化数据结构,每次修改后我们都会得到一个新的版本,且旧版本可以完好保留。
结构共享
我们新生成一个根节点,对于有修改的部分,把相应路径上的所有节点重新生成,对于本次操作没有修改的部分,我们可以直接把相应的旧的节点拷贝过去,这其实就是结构共享
意义
意义在于它弥补了 Javascript 没有不可变数据结构的问题。不可变数据结构是函数式编程中必备的。前端工程师被 OOP 洗脑太久了,组件根本上就是函数用法,FP 的特点更适用于前端开发。
使用 Object 获取 API
const immutable = require("immutable");
Object.keys(immutable);
- "default",
- "version",
- "Collection",
- "Iterable",
- "Seq",
- "Map",
- "OrderedMap",
- "List",
- "Stack",
- "Set",
- "OrderedSet",
- "Record",
- "Range",
- "Repeat",
- "is",
- "fromJS" 将一个 JavaScript 对象转换为 immutable 对象
- "hash",
- "isImmutable",
- "isCollection",
- "isKeyed",
- "isIndexed",
- "isAssociative",
- "isOrdered",
- "isValueObject",
- "get", 从 immutable 对象中获取数据
- "getIn",可以获取嵌套的 immutable 对象数据
- "has",
- "hasIn",
- "merge",
- "mergeDeep",
- "mergeWith",
- "mergeDeepWith",
- "remove",
- "removeIn",
- "set",
- "setIn",
- "update",
- "updateIn"
fromJS 将一个 JavaScript 对象转换成 Map, 获取 Map 数据结构中的数据
const immutable = require("immutable");
const immutableObj = immutable.fromJS({ a: 1, b: 2, c: 3 });
console.log(immutableObj); // Map { "a": 1, "b": 2, "c": 3 }
const aa = immutableObj.get("a"); // 1
const bb = immutableObj.get("b"); // 2
const cc = immutableObj.get("c"); // 3
// 具有嵌套结构的数据
const dd = immutable.FromJS({ x: { y: { z: "this nest value" } } });
const ddz = dd.getIn("z"); // "this nest value"
Map 对象具有 map 能够调用 map 方法, 注意,我们这里的 Map 和 ES6+ 中的 Map 是有很大的差异的。ES6+ 的 Map 的遍历方法是没有 map 方法的,但是有 forEach 方法。immutablejs 中将对象转化成 immutablejs 的 Map 之后的 Map 对象是没有。