268

私はこれを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)か?

4

14 に答える 14

163

配列の長さしかわからず、アイテムを変換する必要があるというタスクがありました。私はこのようなことをしたかった:

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

このような配列をすばやく作成するには:

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

しかし、それはうまくいきませんでした:(Jonathan Lonowskiの答えを上記のいくつかの答えを参照してください)

解決策は、 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));

于 2016-01-29T13:57:59.270 に答える
146

最初の例は

x = new Array(3);

長さが3で要素がない配列を作成するため、インデックス[0]、[1]、および[2]は作成されません。

そして2つ目は、3つの未定義のオブジェクトを含む配列を作成します。この場合、それら自体が作成するインデックス/プロパティですが、それらが参照するオブジェクトは未定義です。

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

マップは、設定された長さではなく、インデックス/プロパティのリストで実行されるため、インデックス/プロパティが作成されていない場合、マップは実行されません。

于 2011-03-31T14:45:57.347 に答える
105

ES6を使用すると[...Array(10)].map((a, b) => a)、すばやく簡単に実行できます。

于 2016-10-05T16:20:16.037 に答える
29

ES6ソリューション:

[...Array(10)]

ただし、typescript(2.3)では機能しません

于 2017-05-05T06:22:51.787 に答える
23

MDCページからmap

[...]callbackは、値が割り当てられている配列のインデックスに対してのみ呼び出されます。[...]

[undefined]map実際には、反復するようにインデックスにセッターを適用しnew Array(1)ますが、デフォルト値のundefinedsoでインデックスを初期化するだけでmapスキップします。

これはすべての反復法で同じだと思います。

于 2011-03-31T14:47:10.827 に答える
20

配列は異なります。違いはnew Array(3)、長さが3でプロパティがない[undefined, undefined, undefined]配列を作成するのに対し、「0」、「1」、「2」と呼ばれる3つのプロパティの長さでそれぞれ値が。の配列を作成することですundefinedin演算子を使用して違いを確認できます。

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

undefinedこれは、JavaScriptでネイティブオブジェクトの存在しないプロパティの値を取得しようとすると、(存在しない変数を参照しようとしたときに発生するようなエラーをスローするのではなく)返されるという少し紛らわしい事実に起因します。 )、これは、プロパティが以前に明示的にに設定されている場合に得られるものと同じですundefined

于 2011-03-31T14:48:48.113 に答える
11

他の回答で徹底的に説明されている理由により、Array(n).map機能しません。ただし、ES2015ではArray.fromマップ関数を受け入れます。

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

于 2020-03-26T09:35:44.950 に答える
9

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を参照してください。

配列のメソッド、、、、、、、はmap、内部メソッドeveryによってインデックスプロパティをチェックするため、コールバックを呼び出しません。someforEachslicereducereduceRightfilterHasPropertynew 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);
于 2017-04-18T08:16:18.733 に答える
7

これを説明する最良の方法は、Chromeがそれを処理する方法を調べることだと思います。

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

つまり、実際に起こっているのは、new Array()が長さ3の空の配列を返しているが、値は返さないということです。したがって、技術的に空のアレイで実行する場合、x.map設定するものは何もありません。

undefinedFirefoxは、値がない場合でも、これらの空のスロットを「埋める」だけです。

これは明らかにバグではなく、何が起こっているのかを表現するための貧弱な方法だと思います。Chromeは、実際には配列に何も存在しないことを示しているため、「より正確」だと思います。

于 2011-03-31T14:47:52.590 に答える
5

バグではありません。これが、Arrayコンストラクターが機能するように定義されている方法です。

MDCから:

Arrayコンストラクターで単一の数値パラメーターを指定するときは、配列の初期長を指定します。次のコードは、5つの要素の配列を作成します。

var billingMethod = new Array(5);

配列コンストラクターの動作は、単一のパラメーターが数値であるかどうかによって異なります。

この.map()メソッドには、値が明示的に割り当てられている配列の要素のみが含まれます。の明示的な割り当てでさえ、undefined値が反復に含める資格があると見なされます。undefinedこれは奇妙に思えますが、本質的には、オブジェクトの明示的なプロパティと欠落しているプロパティの違いです。

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

オブジェクトxには「z」というプロパティがなく、オブジェクトにyはあります。ただし、どちらの場合も、プロパティの「値」はであるように見えますundefined。配列の場合も同様です。の値はlength、0から。までのすべての要素に暗黙的に値の割り当てを実行しますlength - 1。したがって.map()、Arrayコンストラクターと数値引数を使用して新しく構築された配列で呼び出された場合、関数は何も実行しません(コールバックを呼び出しません)。

于 2011-03-31T14:46:52.940 に答える
4

ちょうどこれに遭遇しました。使えると便利でしょうArray(n).map

Array(3)おおよその収量{length: 3}

[undefined, undefined, undefined]番号付きのプロパティを作成します
{0: undefined, 1: undefined, 2: undefined, length: 3}

map()の実装は、定義されたプロパティに対してのみ機能します。

于 2013-10-29T02:01:55.550 に答える
3

配列を値で簡単に埋めるためにこれを行っている場合、ブラウザのサポート上の理由でfillを使用できず、実際にforループを実行したくないx = new Array(3).join(".").split(".").map(...場合は、空の配列を提供することもできます。文字列。

かなり醜いですが、少なくとも問題と意図は非常に明確に伝えられています。

于 2016-03-21T14:42:49.990 に答える
1

問題は理由であるため、これはJSがどのように設計されたかに関係しています。

この振る舞いを説明するために私が考えることができる2つの主な理由があります:

  • パフォーマンス:与えられx = 10000new Array(x)コンストラクターが0から10000までループして配列をundefined値で埋めるのを避けるのが賢明です。

  • 暗黙的に「未定義」:Givea = [undefined, undefined]b = new Array(2)、、a[1]およびb[1]は両方とも戻りますが、範囲外であってundefineda[8]戻りb[8]ます。undefined

最終的に、表記は、明示的に宣言されていないためにとにかく値empty x 3の長いリストを設定および表示することを回避するためのショートカットです。undefinedundefined

注:配列a = [0]とを指定するとa[9] = 9console.log(a)が返さ(10) [0, empty x 8, 9]れ、明示的に宣言された2つの値の差を返すことでギャップが自動的に埋められます。

于 2019-09-15T13:37:12.700 に答える
0

回避策としての簡単なユーティリティメソッドは次のとおりです。

シンプルなmapFor

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]
于 2018-10-10T08:36:12.533 に答える