![](/img/trans.png)
[英]Array.prototype.fill() with object passes reference and not new instance
[英]JavaScript - why Array.prototype.fill actually fills a "pointer" of object when filling anything like 'new Object()'
我試圖使用 Array.prototype.fill 方法來創建 anxn 2D 數組,但代碼有問題,我最終發現里面的所有數組實際上都是指向同一個數組的“指針” 。
樣本:
var matrix = new Array(10).fill(new Array(10), 0);
我從概念上認為這可以創建一個 10 x 10 2D 陣列。 但是,如果我為矩陣 [0][0] 賦值:
matrix[0][0] = 1;
結果實際上是:
matrix[0][0] === 1;
matrix[1][0] === 1;
matrix[2][0] === 1;
matrix[3][0] === 1;
matrix[4][0] === 1;
matrix[5][0] === 1;
matrix[6][0] === 1;
matrix[7][0] === 1;
matrix[8][0] === 1;
matrix[9][0] === 1;
並且每次如果我嘗試為矩陣中的任何位置賦值,其他子數組中的所有相應位置也會發生變化。
我想知道為什么會這樣?
任何人都可以回答這個令人頭疼的問題嗎?
我想知道為什么會這樣?
Array#fill
將您提供的值作為第一個參數,並用該值的副本填充數組。
你給它的值是一個數組的引用,所以自然它給你的是一個充滿該引用副本的數組。 不是數組的副本,是引用的副本。
例如,出於與此代碼完全相同的原因,它會以這種方式運行:
var a = new Array(10);
var b = a;
...留給我們a
和b
都引用同一個數組(都包含相同的值;對我們創建的單個數組的引用)。
讓我們對它進行一些 Unicode 藝術:
這段代碼運行后:
var a = new Array(10);
var b = a;
我們在內存中有這個(減去一些不相關的細節):
a:Ref89895−−−+ | | +−−−−−−−−−−−−−−−+ +−−−−−>| array | | +−−−−−−−−−−−−−−−+ | | length: 10 | b:Ref89895−−−+ +−−−−−−−−−−−−−−−+
a
和b
包含一個引用,我在此處顯示為 Ref89895,盡管我們從未看到實際值。 那是b = a
復制的b = a
,而不是數組本身。
同樣,當您執行以下操作時:
var matrix = new Array(10).fill(new Array(10), 0);
你最終得到
+−−−−−−−−−−−−−−−+ matrix:Ref89895−−−>| array | +−−−−−−−−−−−−−−−+ | length: 10 | | 0: Ref55462 |--\ | 1: Ref55462 |--\\ | 2: Ref55462 |--\\\ | 3: Ref55462 |--\\\\ +−−−−−−−−−−−−−−−+ | 4: Ref55462 |---+++++->| array | | 5: Ref55462 |--///// +−−−−−−−−−−−−−−−+ | 6: Ref55462 |--//// | length: 10 | | 7: Ref55462 |--/// +−−−−−−−−−−−−−−−+ | 8: Ref55462 |--// | 9: Ref55462 |--/ +−−−−−−−−−−−−−−−+
要創建一個 10 位數組,其中 10 位中的每一個本身都是 0 的 10 位數組,我可能會使用Array.from
或fill
map
:
// Array.from
var matrix = Array.from({length: 10}, function() {
return new Array(10).fill(0);
});
// fill and map
var matrix = new Array(10).fill().map(function() {
return new Array(10).fill(0);
});
或在 ES2015 中:
// Array.from
let matrix = Array.from({length: 10}, () => new Array(10).fill(0));
// fill and map
let matrix = new Array(10).fill().map(() => new Array(10).fill(0));
這是因為當您調用array.fill()
您只向它傳遞了一個new Array()
值。 這就是用單個值填充數組的重點。
如果您想獲取現有數組並為每個索引分配一個新實例,您可以使用.map()
。
var matrix = new Array(10).fill().map(function(item, index, arr) {
return new Array(10);
});
要以嵌套方式執行此操作,您似乎希望可以映射兩次。
// the callback function's item, index and array properties are optional
var matrix = new Array(10).fill().map(function(item, index, arr) {
return new Array(10).fill(0);
});
如果您使用 ES6,這可以進一步簡化為單行。
var matrix = new Array(10).fill().map(() => new Array(10).fill(0));
.map()
起作用的原因是它為數組中的每個項目調用提供的回調函數。
.map()
.fill()
之前的空白.fill()
的原因是用值填充數組。 默認情況下,給定大小的新數組undefined
為每個項目undefined
。 .fill()
將臨時填充數組,以便它可以被映射。
在您的特定情況下,由於Array.prototype.fill()
填充單個值,並且在 JS 對象(例如數組)中是引用類型,因此所有索引都帶有對第一個的引用。 在 JS 中創建 ND 數組可能並不像看起來那么簡單,因為數組將通過引用進行復制。 對於通用arrayND
函數,您需要一個數組克隆實用程序函數。 讓我們來看看;
Array.prototype.clone = function(){ return this.map(e => Array.isArray(e) ? e.clone() : e); }; function arrayND(...n){ return n.reduceRight((p,c) => c = (new Array(c)).fill().map(e => Array.isArray(p) ? p.clone() : p )); } var array3D = arrayND(5,5,5,"five"); array3D[0][1][2] = 5; console.log(array3D);
arrayND 函數采用無限數量的參數。 最后一個指定填充我們的 ND 數組的默認值,前面的參數是它們代表的每個維度的大小。 (第一個參數是維度 1,第二個參數是維度 2 等等...)
通過遞歸,您可以創建更多維度;)
const range = r => Array(r).fill().map((v, i) => i); const range2d = (x, y) => range(x).map(i => range(y)); const rangeMatrix = (...ranges) => (function ranger(ranged) { return ranges.length ? ranger(range(ranges.pop()).map(i => ranged)) : ranged })(range(ranges.pop())); let arr10x10 = range2d(10, 10); let arr3x5 = range2d(3, 2); let arr4x3 = rangeMatrix(4, 3); let arr4x3x2 = rangeMatrix(4, 3, 2); let arr4x3x2x5 = rangeMatrix(4, 3, 2, 5); console.log(arr10x10); console.log(arr3x5); console.log(arr4x3); console.log(arr4x3x2); console.log(arr4x3x2x5);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.