簡體   English   中英

JavaScript - 為什么 Array.prototype.fill 在填充諸如“new Object()”之類的任何內容時實際上會填充對象的“指針”

[英]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;

...留給我們ab都引用同一個數組(都包含相同的;對我們創建的單個數組的引用)。

讓我們對它進行一些 Unicode 藝術:

這段代碼運行后:

var a = new Array(10);
var b = a;

我們在內存中有這個(減去一些不相關的細節):

a:Ref89895−−−+
             |
             |      +−−−−−−−−−−−−−−−+
             +−−−−−>|     array     |
             |      +−−−−−−−−−−−−−−−+
             |      | length: 10    |
b:Ref89895−−−+      +−−−−−−−−−−−−−−−+

ab包含一個引用,我在此處顯示為 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.fromfill 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM