陣列 - Javascript 變數參考錯誤?
我遇到了一個奇怪的問題,我認為這與 Javascript 參考變數的方式有關。我正在進行的專案是 Typescript,但這個功能來自原生 Javascript。
我在程式碼的上方設置了一個物件 all_obj。
以下程式碼:
all = [];
data = [];
console.log(all_obj);
輸出:
{
"1593561600000": {
"date": 1593561600000,
"volume": 24463,
"value": 165049285,
"rank": 0
},
"1596240000000": {
"date": 1596240000000,
"volume": 24366,
"value": 158841976,
"rank": 0
},
"1604188800000": {
"date": 1604188800000,
"volume": 30034,
"value": 196655815,
"rank": 0
},
This goes on 9 more times with similar objects
}
這個,運作如預期。
然而,以下程式碼不知怎的記錄了完全不同的內容:
let all = [];
let data = [];
console.log(all_obj);
for (let key of Object.keys(all_obj)) {
all.push(all_obj[key]);
}
this.data.push(
{
brand: 'all_aggregated',
datapoints: [...all],
},
{
brand: 'all_mean',
datapoints: [...all].map((dp: brand_datapoint) => {
dp.value = +(dp.value / dp.volume).toFixed(2);
return dp;
}),
}
);
它記錄了這個:
{
"1593561600000": {
"date": 1593561600000,
"volume": 24463,
"value": 6746.89,
"rank": 0
},
"1596240000000": {
"date": 1596240000000,
"volume": 24366,
"value": 6519,
"rank": 0
},
"1604188800000": {
"date": 1604188800000,
"volume": 30034,
"value": 6547.77,
"rank": 0
},
9 more like this
}
如你所見,在第二個範例中,all_obj 的子項目中的 "value" 現在是以千為單位,而不是數億。此外,它被格式化到小數點後兩位。這令人懷疑,這正是我在第二個程式碼範例結尾部分這一行所做的格式化:
dp.value = +(dp.value / dp.volume).toFixed(2);
更改這些值究竟是如何向上回傳到程式碼的???
尤其是透過[…全部]
這是在一個 Angular 專案中運行的。我已經刪除了多餘的部分,如果你需要更多資訊,當然可以問,但我不想在這裡貼上 300 行程式碼 :)
我已經為這個問題奮鬥了大約六個小時,並且已經用大約十五種不同的方式完全重寫了相同的程式碼。所以我完全不明白這是什麼原因造成的。
解決方案! 問題不是資料會回傳到 all_obj,而是值被設定在粉紅色箭頭,而紅色箭頭會撿到這些值,因為它們只是參考到它。我以為 [...all] 會解決它,但那只是建立了一個擁有相同參考的新陣列。
答案#1
問題是你正在編輯同一個你一開始使用的物件上的 value。
當你執行 all.push(all_obj[key]); 時,你是在用許多對 all_obj 中內部物件的引用來填充 all。由於這些只是引用,與它們互動會改變 相同的物件,就像在 all_obj 中的那些一樣。所以當你設置值 dp.value = 時,它會「向上傳遞回程式碼」,因為它是與 all_obj 中的物件相同的物件。
由於你只是基於格式原因想要對新對象進行這些更改,你應該先複製該對象,然後只在複製的對象中編輯數值。由於這些數據點似乎很簡單,屬性只有簡單的數字,你可以輕鬆地複製它。
dp = {...dp};
你應該在更改值之前先這樣做。
你可能會認為這是不需要的,因為有 [...all]。然而,那只會複製 all 列表本身,而不是所有的內容。基本上,它是一個「新列表」,包含所有相同對象的相同引用。
你可能對「JavaScript 是傳址還是傳值的語言?」感興趣。
編輯:
再看一次,可能在建立 all 陣列時就直接複製會更好/更乾淨。所以下面不用這樣做
all.push(all_obj[key]);
你可以做
all.push({...all_obj[key]});
那麼在操作點以進行格式或顯示時,你就不需要擔心更改原始資料。
