Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/458.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当JavaScript语言没有';没有提供任何明显的实现不变性的方法吗?_Javascript_Functional Programming_Immutability - Fatal编程技术网

当JavaScript语言没有';没有提供任何明显的实现不变性的方法吗?

当JavaScript语言没有';没有提供任何明显的实现不变性的方法吗?,javascript,functional-programming,immutability,Javascript,Functional Programming,Immutability,所以我决定试用函数式编程,因为人们说它能让你更好地控制你编写的程序,它是非常可测试的,而且它能让代码更可读,更容易理解——谁会不想要呢 显然,我决定尝试使用函数范式编写程序。几乎就在我决定写这篇文章的时候,我被卡住了。我真的想不出如何使程序的所有数据都不可变。我的第一个想法是使用所有const声明的变量,因为const变量在初始赋值后不能重新引用;我发现实现一个不可变的数据结构与声明几个常量变量来存储程序状态是完全不同的。我很清楚,我需要不可变的对象,具有不可变的属性,等等。尝试创建不可变的数据

所以我决定试用函数式编程,因为人们说它能让你更好地控制你编写的程序,它是非常可测试的,而且它能让代码更可读,更容易理解——谁会不想要呢

显然,我决定尝试使用函数范式编写程序。几乎就在我决定写这篇文章的时候,我被卡住了。我真的想不出如何使程序的所有数据都不可变。我的第一个想法是使用所有
const
声明的变量,因为
const
变量在初始赋值后不能重新引用;我发现实现一个不可变的数据结构与声明几个常量变量来存储程序状态是完全不同的。我很清楚,我需要不可变的对象,具有不可变的属性,等等。尝试创建不可变的数据结构后(示例位于问题的底部)。我的结论是JavaScript并没有实现不可变数据的方法,至少不是一种显而易见的方法。现在我在这里,抓着我的头

我想知道的是:

“编写JavaScript程序的人如何使用函数式编程范式来实现不可变的数据结构,而这种语言并没有提供任何实现不可变的方式?”

编辑: 我想说得尽可能清楚,因为我在过去的一些问题中一直在努力澄清,所以我只想解释一下我的要求:

简而言之,我想知道的是: 一个人如何实现适用于函数式编程范式的不可变数据结构。任何不可变数据结构的示例就足够了,甚至可以是一个不可变对象。如果答案涉及使用第三方库、其他语言或任何其他很棒的工具,以及使用这些工具的代码的示例好的,那就太好了


下面是一些代码,我试图将一个JS对象变成不可变的。这不是一个好代码,事实上它抛出了一个错误,但它展示了我的心态,以及我试图解决的问题


"严格使用",;
常量obj={};
对象定义属性(obj{
提案1:{
值:(str)=>{this.prop_3=str};
可写:false,
},
提案2:{
值:()=>this.prop_3;
可写:false,
},
提案3:{
值:“”,
可写:false,
},
});
obj.第1号提案(“苹果和香蕉”);
控制台日志(对象属性3);
/*
终端输出:
调试器已附加。
正在等待调试器断开连接。。。
file:///home/ajay/Project-Repos/j-commandz/sandbox.js:19
this.prop_3=str;
^
TypeError:无法分配给对象“”的只读属性“”prop#3“”
at Object.set(file:///home/ajay/Project-Repos/j-commandz/sandbox.js:19:19)
在file:///home/ajay/Project-Repos/j-commandz/sandbox.js:37:5
*/

欢迎使用函数式编程

一种解决方案是使用ES6类。getter返回属性的深度副本,setter抛出错误

示例代码:

班级人员{
_name=“”;
建造师(姓名){
这个。_name=name;
}
获取名称(){
返回此.name;
}
集合名称(名称){
抛出新错误(“无法重新分配某人的姓名”);
}
}
类不可变数组{
_arr=[];
建造师(arr){
这个;
}
获取arr(){
返回[这个];
}
设置arr(arr){
抛出新错误(“无法重新分配不可变数组”);
}
}
const aPerson=新人(“jiho”);
aPerson.name=“W3Dojo”;//错误:无法重新分配人名
const aImmutableArray=新的ImmutableArray([1,2,3]);
aImmutableArray.arr=[2];//错误:无法重新分配ImmutableArray
const arr=aImmutableArray.arr;
arr[2]=20;
console.log(aImmutableArray.arr[2]);//3
JavaScript实际上没有实现不可变数据的方法,至少不是一种显而易见的方法

您可能不熟悉JS,但使对象不可变的明显方法是:

如果对象是不可变的,我们需要用我们想要的新值创建新的对象,而不是将其赋值给它们的属性。在对象中,文本帮助我们实现这一点,帮助器方法也是如此:

const base = {
  withProp(newVal) {
    return { withProp: this.withProp, prop: newVal };
  },
  prop: '';
};

const obj1 = base.withProp('apples & bananas');
console.log(obj1.prop);
const obj2 = {...base, prop: obj1.prop + ' & oranges'};
console.log(obj2.prop);
有了足够的自我约束(或钻取、代码审查,或类似于TypeChecker和Linter的工具),这种克隆对象的编程风格就变得自然了,您就不会再因意外分配而出错了

当然,使用更复杂(嵌套)的结构很麻烦,因此有相当多的库提供帮助函数,还有一些更高级的实现,比每次克隆整个数据更有效。

尽管JavaScript缺少不可变的内置数据结构,但不可变状态仍然是可能的。 正如您所知,变量存储程序的状态。像Lisp这样的函数式语言通常通过将当前状态作为输入并返回新的更新状态作为输出(作为另一个函数的输入;重复)来更改程序状态

JavaScript程序通常通过改变变量来改变程序状态,但也可以使用上述Lisp使用的方法。与其编写改变变量的函数,只需编写输入当前状态并返回新输出状态的函数,而无需修改任何输入

在JavaScript中以不可变样式编程时,可能会遇到一些缺点:

  • JavaScript是针对变异而不是不变性进行优化的。因此可能存在内存/性能损失。不变性范式更喜欢生成新值而不是变异现有值。(另一方面,类似Lisp的语言是针对不变性而不是变异进行优化的。)。
    const obj = Object.freeze({
      prop_2() { return this.prop_3 }
      prop_3: '',
    });
    
    obj.prop_3 = 'apples & bananas'; // throws as expected
    console.log(obj.prop_3);
    
    const base = {
      withProp(newVal) {
        return { withProp: this.withProp, prop: newVal };
      },
      prop: '';
    };
    
    const obj1 = base.withProp('apples & bananas');
    console.log(obj1.prop);
    const obj2 = {...base, prop: obj1.prop + ' & oranges'};
    console.log(obj2.prop);
    
    const input = 'Hello World';
    const output = input.toUpperCase();
    
    console.log(input === output); // false
    
    class User {
      name;
    
      setName(value) { this.name = value }
    }
    
    
    const user = { name: 'Giuseppe' };
    
    const setUserName = (name, user) => ({ ...user, name });
    
    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