[英]How to keep an array with objects immutable in javascript?
我想基於兩個數組創建一個數組 - “ideaList”和“endorsements”全局聲明。 由於ideaList 和endorsements 用於程序的其他部分,我需要它們是不可變的,我認為 .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.logideaList的時候,注意到數組的對象都有“count”、“like”、“position”屬性和值,證明數組已經發生了變異。
我嘗試僅使用 .map,但結果相同。
你知道我如何防止ideaList發生變異嗎? 另外我想避免使用const,因為我首先全局聲明ideaList,然后在另一個函數中為其分配一些數據。
你不是在改變數組本身,而是改變數組包含引用的對象。 .map()
創建數組的副本,但其中包含的引用指向與原始對象完全相同的對象,您已通過直接向它們添加屬性而對其進行了變異。
您還需要制作這些對象的副本並將屬性添加到這些副本中。 一個巧妙的方法是在.map()
回調中使用對象傳播:
.map(({ ...obj }) => {
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
如果您的環境不支持對象傳播語法,請使用Object.assign()
克隆對象:
.map(originalObj => {
const obj = Object.assign({}, originalObj);
obj.count = endorsements
.filter(x=>x.ideaNumber===obj.ideaNumber)
...
為了幫助牢記不變性,您可以將您的值視為原始值。
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 */ })
有一些庫可以幫助您處理不可變模式:
在 JS 中,對象是被引用的。 換句話說,在創建時,您將對象變量指向一個打算保存有意義值的內存位置。
var o = {foo: 'bar'}
變量 o 現在指向具有{foo: bar}
的內存。
var p = o;
現在變量p
也指向相同的內存位置。 所以,如果你改變o
,它也會改變p
。
這就是你的函數內部發生的事情。 即使您使用不會改變其值的 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
或 latest spread 運算符來完成。
a = [{foo: 1}];
b = a.map(o => {
var p = {...o}; //Create a new object
p.foo = 2;
return p;
})
console.log(a); // {foo:1}
您在地圖功能中分配這些屬性,您需要更改它。 (只需聲明一個空對象,而不是使用您當前的obj
)
你可以使用新的 ES6 內置不變性機制,或者你可以像這樣在你的對象周圍包裹一個很好的 getter
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。
制作可變對象“副本”的一種簡單方法是將它們字符串化為另一個對象,然后將它們解析回一個新數組。 這對我有用。
function returnCopy(arrayThing) {
let str = JSON.stringify(arrayThing);
let arr = JSON.parse(str);
return arr;
}
您使用freeze
方法提供要使其不可變的對象。
const person = { name: "Bob", age: 26 }
Object.freeze(person)
其實可以使用spread opreator讓原數組保持不變, y
下面是不可變數組的例子:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = [...y]; // make array y as immutable array [1,2,3,4,5]
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
如果您不使用擴展運算符,當循環運行時,y 數組將被更改。
這是可變數組結果:
const y = [1,2,3,4,5];
function arrayRotation(arr, r, v) {
for(let i = 0; i < r; i++) {
arr = y; // this is mutable, because arr and y has same memory address
arr.splice(i, 0, v);
arr.pop();
console.log(`Rotation ${i+1}`, arr);
}
}
arrayRotation(y, 3, 5)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.