簡體   English   中英

JavaScript “new Array(n)” 和 “Array.prototype.map” 怪異

[英]JavaScript "new Array(n)" and "Array.prototype.map" weirdness

我在 Firefox-3.5.7/Firebug-1.5.3 和 Firefox-3.6.16/Firebug-1.6.2 中觀察到了這一點

當我啟動 Firebug 時:

 var x = new Array(3) console.log(x) // [undefined, undefined, undefined] var y = [undefined, undefined, undefined] console.log(y) // [undefined, undefined, undefined] console.log( x.constructor == y.constructor) // true console.log( x.map(function() { return 0; }) ) // [undefined, undefined, undefined] console.log( y.map(function() { return 0; }) ) // [0, 0, 0]

這里發生了什么? 這是一個錯誤,還是我誤解了如何使用new Array(3)

我有一個任務,我只知道數組的長度,需要轉換項目。 我想做這樣的事情:

let arr = new Array(10).map((val,idx) => idx);

要快速創建這樣的數組:

[0,1,2,3,4,5,6,7,8,9]

但它沒有奏效,因為:(見喬納森·洛諾夫斯基的回答下面的幾個答案)

解決方案可能是使用Array.prototype.fill()用任何值(即使未定義)填充數組項

let arr = new Array(10).fill(undefined).map((val,idx) => idx);

 console.log(new Array(10).fill(undefined).map((val, idx) => idx));

更新

另一種解決方案可能是:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);

 console.log(Array.apply(null, Array(10)).map((val, idx) => idx));

看來第一個例子

x = new Array(3);

創建一個長度為 3 但沒有任何元素的數組,因此不會創建索引 [0]、[1] 和 [2]。

第二個創建一個包含 3 個未定義對象的數組,在這種情況下,它們自己創建了索引/屬性,但它們引用的對象是未定義的。

y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];

由於 map 在索引/屬性列表上運行,而不是在設置的長度上運行,因此如果沒有創建索引/屬性,它將不會運行。

使用 ES6,您可以快速簡單地執行[...Array(10)].map((a, b) => a)

ES6 解決方案:

[...Array(10)]

但不適用於打字稿(2.3)

map的 MDC 頁面:

[...] callback僅針對具有賦值的數組索引調用; [...]

[undefined]實際上在 index(es) 上應用 setter,以便map進行迭代,而new Array(1)只是使用默認值undefined初始化 index(es),因此map跳過它。

我相信這對於所有迭代方法都是一樣的。

數組不同。 不同之處在於new Array(3)創建了一個長度為三個但沒有屬性的數組,而[undefined, undefined, undefined]創建了一個長度為三個的數組和三個屬性,分別稱為“0”、“1”和“ 2",每個都有一個undefined的值。 您可以使用in運算符查看差異:

"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true

這源於一個稍微令人困惑的事實,即如果您嘗試獲取 JavaScript 中任何本機對象的不存在屬性的值,它會返回undefined (而不是拋出錯誤,就像您嘗試引用不存在的對象時發生的那樣)變量),這與之前已將屬性顯式設置為undefined時獲得的結果相同。

由於其他答案中徹底解釋了原因, Array(n).map不起作用。 然而,在 ES2015 Array.from接受一個 map 函數:

 let array1 = Array.from(Array(5), (_, i) => i + 1) console.log('array1', JSON.stringify(array1)) // 1,2,3,4,5 let array2 = Array.from({length: 5}, (_, i) => (i + 1) * 2) console.log('array2', JSON.stringify(array2)) // 2,4,6,8,10

在 ECMAScript 第 6 版規范中。

new Array(3)只定義屬性length ,不定義像{length: 3}這樣的索引屬性。 請參閱https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len步驟 9。

[undefined, undefined, undefined]將定義索引屬性和長度屬性,如{0: undefined, 1: undefined, 2: undefined, length: 3} 請參閱https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList步驟 5。

Array 的方法map , every , some , forEach , slice , reduce , reduceRight , filter會通過HasProperty內部方法檢查 index 屬性,所以new Array(3).map(v => 1)不會調用回調。

有關更多詳細信息,請參閱https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map

怎么修?

let a = new Array(3);
a.join('.').split('.').map(v => 1);

let a = new Array(3);
a.fill(1);

let a = new Array(3);
a.fill(undefined).map(v => 1);

let a = new Array(3);
[...a].map(v => 1);

我認為解釋這一點的最佳方法是查看 Chrome 處理它的方式。

>>> x = new Array(3)
[]
>>> x.length
3

所以實際發生的是 new Array() 返回一個長度為 3 但沒有值的空數組。 因此,當您在技術上為空的數組上運行x.map時,無需設置任何內容。

Firefox 只是用undefined來“填充”那些空槽,即使它沒有值。

我不認為這明確是一個錯誤,只是表示正在發生的事情的一種糟糕的方式。 我想 Chrome 是“更正確的”,因為它表明數組中實際上沒有任何東西。

不是錯誤。 這就是定義 Array 構造函數的工作方式。

來自 MDC:

當您使用 Array 構造函數指定單個數值參數時,您指定了數組的初始長度。 以下代碼創建一個包含五個元素的數組:

var billingMethod = new Array(5);

Array 構造函數的行為取決於單個參數是否為數字。

.map()方法僅包括在數組的迭代元素中明確分配了值的元素。 即使是undefined的顯式賦值也會導致一個值被認為有資格包含在迭代中。 這看起來很奇怪,但這本質上是對象上顯式undefined屬性和缺失屬性之間的區別:

var x = { }, y = { z: undefined };
if (x.z === y.z) // true

對象x沒有名為“z”的屬性,而對象y有。 但是,在這兩種情況下,屬性的“值”似乎都是undefined 在數組中,情況類似: length的值確實隱含地對從零到length - 1的所有元素執行值分配。 因此,在使用 Array 構造函數和數字參數新建的數組上調用.map()函數時,它不會做任何事情(不會調用回調)。

剛碰到這個。 能夠使用Array(n).map肯定會很方便。

Array(3)大約產生{length: 3}

[undefined, undefined, undefined]創建編號屬性:
{0: undefined, 1: undefined, 2: undefined, length: 3}

map() 實現只作用於定義的屬性。

如果您這樣做是為了輕松地用值填充數組,由於瀏覽器支持的原因不能使用填充,並且真的不想執行 for 循環,您也可以執行x = new Array(3).join(".").split(".").map(...這將為您提供一個空字符串數組。

我不得不說這很丑,但至少問題和意圖已經很清楚地傳達了。

既然問題是為什么,這與 JS 的設計方式有關。

我可以想到解釋這種行為的兩個主要原因:

  • 性能:給定x = 10000new Array(x)構造函數避免從 0 到 10000 循環以用undefined的值填充數組是明智的。

  • 隱式“未定義”:給a = [undefined, undefined]b = new Array(2)a[1]b[1]都將返回undefined ,但a[8]b[8]也將返回undefined即使它們超出范圍。

最終,符號empty x 3是避免設置和顯示一長串undefined值的捷徑,因為它們沒有顯式聲明,所以無論如何都是undefined的。

注意:給定數組a = [0]a[9] = 9console.log(a)將返回(10) [0, empty x 8, 9] ,通過返回兩個值之間的差異自動填補空白明確聲明。

這是一個簡單的實用程序方法作為解決方法:

簡單的地圖

 function mapFor(toExclusive, callback) { callback = callback || function(){}; var arr = []; for (var i = 0; i < toExclusive; i++) { arr.push(callback(i)); } return arr; }; var arr = mapFor(3, function(i){ return i; }); console.log(arr); // [0, 1, 2] arr = mapFor(3); console.log(arr); // [undefined, undefined, undefined]

完整示例

這是一個更完整的示例(帶有完整性檢查),它還允許指定可選的起始索引:

 function mapFor() { var from, toExclusive, callback; if (arguments.length == 3) { from = arguments[0]; toExclusive = arguments[1]; callback = arguments[2]; } else if (arguments.length == 2) { if (typeof arguments[1] === 'function') { from = 0; toExclusive = arguments[0]; callback = arguments[1]; } else { from = arguments[0]; toExclusive = arguments[1]; } } else if (arguments.length == 1) { from = 0; toExclusive = arguments[0]; } callback = callback || function () {}; var arr = []; for (; from < toExclusive; from++) { arr.push(callback(from)); } return arr; } var arr = mapFor(1, 3, function (i) { return i; }); console.log(arr); // [1, 2] arr = mapFor(1, 3); console.log(arr); // [undefined, undefined] arr = mapFor(3); console.log(arr); // [undefined, undefined, undefined]

倒計時

操作傳遞給回調的索引允許向后計數:

var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
    return count - 1 - i;
});
// arr = [2, 1, 0]

在Chrome中,如果我執行new Array(3) ,則會得到[] ,因此我的猜測是您遇到了瀏覽器錯誤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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