JΛY*_*ÐΞV 2 javascript functional-programming immutability
我决定尝试使用函数式编程范式,因为我从几个来源听说并阅读了函数式编程创建的代码:
……谁不想要?我当然试过了。
我对函数式编程的第一次尝试并不顺利。我从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
*/
Run Code Online (Sandbox Code Playgroud)
你是对的,Javascript(与Haskell & co. 不同)没有为不可变数据结构提供一流的支持(在 Java 中你会有关键字final)。这并不意味着您不能以不可变的方式编写代码或推理程序。
正如其他人所提到的,您仍然有一些本机 javascript API 可以帮助您实现不变性(ish),但是正如您已经意识到的那样,它们都没有真正解决问题(Object.freeze只能在浅层工作,const阻止您重新分配变量,但不能改变它, 等等。)。
我想提前道歉,因为这个答案可能主要基于意见,并且不可避免地会因我自己的经验和思维方式而有缺陷。所以,请用少许盐选择以下内容,因为这只是我在这个主题上的两分钱。
我想说的是不变性主要是一种思维状态,在此之上您可以构建所有支持(或使其更易于使用)它的语言 API。
我之所以说“这主要是一种心态”,是因为您可以(某种程度上)用第三方库来弥补一流语言结构的不足(并且有一些非常令人印象深刻的成功案例)。
嗯,它背后的想法是,任何变量都被视为固定的,任何突变都必须在新实例中解决,而原始实例input保持不变。
好消息是,这已经适用于所有 javascript 原语。
const input = 'Hello World';
const output = input.toUpperCase();
console.log(input === output); // false
Run Code Online (Sandbox Code Playgroud)
所以,问题是,我们如何能对待一切,因为它是一个原始的?
...嗯,答案很简单,接受函数式编程的一些基本原则,并让第三方库填补这些语言空白。
state从他们的transition逻辑中分离出来:class User {
name;
setName(value) { this.name = value }
}
Run Code Online (Sandbox Code Playgroud)
只是
const user = { name: 'Giuseppe' };
const setUserName = (name, user) => ({ ...user, name });
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
Ramda提供不变性,并用您通常在任何f语言中找到的所有声明性好东西来丰富 js api (sanctuary-js和fp-ts也是伟大的成功案例)RxJS 使用序列实现不可变和无副作用的编程,同时还提供惰性评估机制等。Redux并XState为不可变状态管理提供解决方案。const input = 'Hello World';
const output = input.toUpperCase();
console.log(input === output); // false
Run Code Online (Sandbox Code Playgroud)
class User {
name;
setName(value) { this.name = value }
}
Run Code Online (Sandbox Code Playgroud)
const user = { name: 'Giuseppe' };
const setUserName = (name, user) => ({ ...user, name });
Run Code Online (Sandbox Code Playgroud)
如您所知,变量存储程序的状态。像 Lisp 这样的函数式语言通常通过将当前状态作为输入并返回新的更新状态作为输出(用作另一个函数的输入;重复)来更改程序状态。
JavaScript 程序通常通过改变变量来改变程序状态,但也可以使用上述 Lisp 使用的方法。无需编写改变变量的函数,只需编写输入当前状态并返回新输出状态的函数,而不修改任何输入。
在 JavaScript 中以不可变风格进行编程时,您可能会遇到一些缺点:
Array.sort()),则突变可能会“泄漏”到您的程序中。Immer是一个 JS 库,有助于在 JS 中使用不可变状态:
基本思想是,您将把所有更改应用到临时的 DraftState,它是 currentState 的代理。一旦完成所有突变,Immer 将根据草稿状态的突变生成 nextState。这意味着您只需修改数据即可与数据进行交互,同时保留不可变数据的所有优点。
Immutable.js是另一个 JS 库,有助于在 JS 中实现不可变状态:
Immutable.js 提供了许多持久不可变数据结构,包括:List、Stack、Map、OrderedMap、Set、OrderedSet 和 Record。
这些数据结构在现代 JavaScript VM 上非常高效,通过使用由 Clojure 和 Scala 推广的哈希映射尝试和向量尝试进行结构共享,最大限度地减少了复制或缓存数据的需要。
Mori提取了 ClojureScript 的优化不可变数据结构,以便您可以在 vanilla JS 中使用它们。(ClojureScript 是一个可以编译为 JavaScript 的 Lisp。)
这个不可变库列表分为两个主要类别:
https://project-awesome.org/stoeffel/awesome-fp-js
ProseMirror 是一个用 JavaScript 编写的编辑器,它使用持久数据结构来存储文档数据。
Object.freeze(),但这通常会被避免,因为它会带来巨大的性能损失。