[英]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)
數組不同。 不同之處在於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 = 10000
和new 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] = 9
, console.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.