網站搜尋

陣列 - 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]});

那麼在操作點以進行格式或顯示時,你就不需要擔心更改原始資料。