当JavaScript没有提供任何明显的实现不变性的方法时,人们如何在JavaScript中实现不可变的数据结构?
我决定尝试使用函数式编程范式,因为我从几个来源听说并阅读了函数式编程创建的代码:
- 我有更多的控制权。
- 很容易测试。
- 易于其他人阅读和关注。
- 对我来说更容易阅读。
……谁不想要?我当然试过了。
我第一次尝试用 JavaScript 进行函数式编程
我对函数式编程的第一次尝试并不顺利。我从state 的角度考虑它,并在整个过程中维护应用程序状态,但是,我写了几行代码后很快就卡住了。我无法解决如何实现真正不可变的不可变数据结构,或者如何在我的数据结构中更改变量,当这些变量及其属性都是不可写时"_
当 JavaScript 不提供任何类型的显式不可变数据类型或支持时,当代 JavaScript 开发人员如何实现不可变数据结构来管理其应用程序的状态?
我正在寻找任何不可变数据结构的示例;以及如何实现允许我使用数据结构的功能来管理 JS 应用程序的状态。如果答案涉及使用 3rd 方库、其他语言或任何其他我认为非常好的工具。一个实际代码的例子会很棒,这样我就可以解释和理解一些东西。
Bellow 是我创建一个可以实现的不可变数据结构的可怕尝试。
虽然它不是很好的代码,但它展示了我想要完成的事情
'use strict';
const obj = {};
Object.defineProperties(obj, {
prop_1: {
value: (str) => {this.prop_3 = str};
writable: false,
},
prop_2: {
value: () => this.prop_3;
writable: false,
},
prop_3: {
value: '',
writable: false,
},
});
obj.prop_1('apples & bananas');
console.log(obj.prop_3);
/*
TERMINAL OUTPUT:
Debugger attached.
Waiting for the debugger to disconnect...
file:///home/ajay/Project-Repos/j-commandz/sandbox.js:19
this.prop_3 = str;
^
TypeError: Cannot assign to read only property 'prop_3' of object '#<Object>'
at Object.set (file:///home/ajay/Project-Repos/j-commandz/sandbox.js:19:19)
at file:///home/ajay/Project-Repos/j-commandz/sandbox.js:37:5
*/
回答
你是对的,Javascript(与Haskell & co. 不同)没有为不可变数据结构提供一流的支持(在 Java 中你会有关键字final)。这并不意味着您不能以不可变的方式编写代码或推理程序。
正如其他人所提到的,您仍然有一些本机 javascript API 可以帮助您实现不变性(ish),但是正如您已经意识到的那样,它们都没有真正解决问题(Object.freeze只能在浅层工作,const阻止您重新分配变量,但不能改变它, 等等。)。
那么,你怎么能做不可变的 JS 呢?
我想提前道歉,因为这个答案可能主要基于意见,并且不可避免地会因我自己的经验和思维方式而有缺陷。所以,请用少许盐选择以下内容,因为这只是我在这个主题上的两分钱。
我想说的是不变性主要是一种思维状态,在此之上您可以构建所有支持(或使其更易于使用)它的语言 API。
我之所以说“这主要是一种心态”,是因为您可以(某种程度上)用第三方库来弥补一流语言结构的不足(并且有一些非常令人印象深刻的成功案例)。
但是不变性是如何工作的呢?
嗯,它背后的想法是,任何变量都被视为固定的,任何突变都必须在新实例中解决,而原始实例input保持不变。
好消息是,这已经适用于所有 javascript 原语。
const input = 'Hello World';
const output = input.toUpperCase();
console.log(input === output); // false
const input = 'Hello World';
const output = input.toUpperCase();
console.log(input === output); // false
所以,问题是,我们如何能对待一切,因为它是一个原始的?
...嗯,答案很简单,接受函数式编程的一些基本原则,并让第三方库
class User {
name;
setName(value) { this.name = value }
}
clas填补这些语const user = { name: 'Giuseppe' }; const setUserName = (name, user) => ({ ...user, name });s User {
name;setName(value) { this.name = value }
}
const user = { name: 'Giuseppe' }; const setUserName = (name, user) => ({ ...user, name });言空白。
state从他们的transition逻辑中分离出来:只是
- 避免命令式方法并利用第三方专用库
import * as R from 'ramda'; const user = { name: 'Giuseppe', address: { city: 'London', } }; const setUserCity = R.assocPath(['address', 'city']); const output = setUserCity('Verbicaro', user); console.log(user === output); // recursively false也许是我喜欢的一些库的注释
Ramda提供不变性,并用您通常在任何f语言中找到的所有声明性好东西来丰富 js api (sanctuary-js和fp-ts也是伟大的成功案例)RxJS使用序列实现不可变和无副作用的编程,同时还提供惰性评估机制等。Redux并XState为不可变状态管理提供解决方案。最后一个例子
最后重申你自己的例子