我想基于两个数组创建一个数组-全局声明的“ ideaList”和“ endorsements”。由于ideaList和背书用于程序的其他部分,因此我需要它们是不可变的,我认为.map和.filter可以保持这种不变性。
function prepareIdeaArray(){
var preFilteredIdeas=ideaList
.filter(hasIdeaPassedControl)
.map(obj => {obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
.reduce((sum, x)=>sum+x.count,0);
obj.like = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber && x.who===activeUser)
.reduce((sum, x)=>sum+x.count,0)===0?false:true
obj.position = generatePosition(obj.status)
obj.description = obj.description.replace(/\n/g, '<br>')
return obj;});
preFilteredIdeas.sort(compareOn.bind(null,'count',false)).sort(compareOn.bind(null,'position',true))
return preFilteredIdeas;
}
但是,当执行此函数后,当我console.log ideaList时,我指出该数组的对象都具有带有值的“ count”,“ like”,“ position”属性,这证明该数组已经被变异。
我仅使用.map尝试过,但结果相同。
您知道我如何防止ideaList变异吗?我也想避免使用const,因为我先全局声明ideaList,然后在另一个函数中为其分配一些数据。
答案 0 :(得分:2)
为帮助您牢记不变性,您可以将值视为原始值。
1 === 2 // false
'hello' === 'world' // false
您也可以将这种思维方式扩展到非原始人
[1, 2, 3] === [1, 2, 3] // false
{ username: 'hitmands' } === { username: 'hitmands' } // false
为了更好地理解它,请查看MDN - Equality Comparisons and Sameness
总是返回给定对象的新实例!
假设我们必须设置待办事项的属性status
。用旧的方式,我们只会做:
todo.status = 'new status';
但是,我们可以通过简单地复制给定对象并返回一个新对象来强制不变性。
const todo = { id: 'foo', status: 'pending' };
const newTodo = Object.assign({}, todo, { status: 'completed' });
todo === newTodo // false;
todo.status // 'pending'
newTodo.status // 'completed'
回到您的示例,而不是做obj.count = ...
,我们只会做:
Object.assign({}, obj, { count: ... })
// or
({ ...obj, count: /* something */ })
有一些库可以帮助您实现不可变模式:
答案 1 :(得分:1)
您不是要更改数组本身,而是要更改数组包含的引用的对象。 public interface IPortableHttpClient
{
Task<HttpResponseMessage> GetAsync(Uri url);
}
创建数组的副本,但其中包含的引用指向与原始对象完全相同的对象,您可以通过直接向其添加属性来对其进行突变。
您还需要制作这些对象的副本,并将属性添加到这些副本中。整齐的方法是在.map()
回调中使用对象散布:
.map()
如果您的环境不支持对象传播语法,请使用 .map(({ ...obj }) => {
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
克隆对象:
Object.assign()
答案 2 :(得分:1)
在JS中,引用了对象。换句话说,创建后,您将获得对象变量以指向要保留有意义值的存储位置。
var o = {foo: 'bar'}
变量o现在指向具有{foo: bar}
的内存。
var p = o;
现在变量p
也指向相同的存储位置。因此,如果您更改o
,它也会更改p
。
这就是函数内部发生的事情。即使您使用不会改变其值的Array方法,但array元素本身是在函数内部进行修改的对象。它创建了一个新的数组-但元素指向的是对象的相同旧存储位置。
var a = [{foo: 1}]; //Let's create an array
//Now create another array out of it
var b = a.map(o => {
o.foo = 2;
return o;
})
console.log(a); //{foo: 2}
一种解决方法是在操作期间为新数组创建一个新对象。可以使用Object.assign
或最新的价差运算符来完成。
a = [{foo: 1}];
b = a.map(o => {
var p = {...o}; //Create a new object
p.foo = 2;
return p;
})
console.log(a); // {foo:1}
答案 3 :(得分:0)
您在地图函数中分配了这些属性,您需要对此进行更改。 (只需声明一个空对象,而不使用当前的obj
)
答案 4 :(得分:0)
您可以使用新的ES6 built-in不变性机制,或者可以像这样围绕对象包装漂亮的吸气剂
var myProvider = {}
function (context)
{
function initializeMyObject()
{
return 5;
}
var myImmutableObject = initializeMyObject();
context.getImmutableObject = function()
{
// make an in-depth copy of your object.
var x = myImmutableObject
return x;
}
}(myProvider);
var x = myProvider.getImmutableObject();
这会将您的对象封闭在全局范围之外,但是getter将可在全局范围内访问。
您可以阅读有关此编码模式here
的更多信息答案 5 :(得分:0)
制作可变对象“副本”的一种简单方法是将它们字符串化为另一个对象,然后将其解析回一个新数组。这对我有用。
function returnCopy(arrayThing) {
let str = JSON.stringify(arrayThing);
let arr = JSON.parse(str);
return arr;
}
答案 6 :(得分:0)
您使用freeze
方法提供要使其不可变的对象。
const person = { name: "Bob", age: 26 }
Object.freeze(person)