Firefox-3.5.7/Firebug-1.5.3およびFirefox-3.6.16/Firebug-1.6.2でこれを確認しました
Firebugを起動すると:
_ >>> x = new Array(3)
[undefined, undefined, undefined]
>>> y = [undefined, undefined, undefined]
[undefined, undefined, undefined]
>>> x.constructor == y.constructor
true
>>> x.map(function(){ return 0; })
[undefined, undefined, undefined]
>>> y.map(function(){ return 0; })
[0, 0, 0]
_
何が起きてる?これはバグですか、それともnew Array(3)
の使用方法を誤解していますか?
最初の例のようです
x = new Array(3);
未定義のポインターを持つ配列を作成します。
2番目は、3つの未定義オブジェクトへのポインターを持つ配列を作成します。この場合、それら自身のポインターは未定義ではなく、それらが指すオブジェクトのみです。
y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];
マップは配列内のオブジェクトのコンテキストで実行されるため、最初のマップは関数の実行にまったく失敗し、2番目のマップは実行に失敗すると考えています。
配列の長さしかわからず、アイテムを変換するために必要なタスクがありました。私はこのようなことをしたかった:
_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));
ES6では、[...Array(10)].map((a, b) => a)
をすばやく簡単に実行できます。
ES6ソリューション:
[...Array(10)]
ただし、TypeScript(2.3)では機能しません。
配列は異なります。違いは、new Array(3)
は長さ3のプロパティを持たない配列を作成するのに対し、[undefined, undefined, undefined]
は、それぞれがundefined
の値を持つ、「0」、「1」、「2」と呼ばれる3つのプロパティと3つのプロパティを持つ配列を作成します。 in
演算子を使用して違いを確認できます。
"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true
これは、JavaScriptでネイティブオブジェクトの存在しないプロパティの値を取得しようとすると、undefined
を返します(参照しようとしたときにエラーがスローされるのではなく)これは、プロパティが以前に明示的にundefined
に設定されている場合に取得するものと同じです。
これを説明する最良の方法は、Chromeがそれを処理する方法を調べることです。
>>> x = new Array(3)
[]
>>> x.length
3
実際に起こっているのは、new Array()が長さ3の空の配列を返しているが、値は返していないということです。したがって、技術的に空の配列でx.map
を実行する場合、設定するものはありません。
Firefoxは、値がなくても、空のスロットにundefined
を「埋める」だけです。
これは明らかにバグではないと思います。何が起こっているかを表現する方法としては不十分です。配列には実際には何も存在しないことが示されているため、Chromeの「より正しい」と思います。
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-arrayaccumulationElementList
ステップ5。
メソッドmap
、every
、some
、forEach
、slice
、reduce
、reduceRight
、配列のfilter
はHasProperty
内部メソッドによってインデックスプロパティをチェックするため、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);
_
ただこれに遭遇しました。 Array(n).map
を使用できると便利です。
Array(3)
はおよそ{length: 3}
[undefined, undefined, undefined]
は、番号付きプロパティを作成します。{0: undefined, 1: undefined, 2: undefined, length: 3}
。
Map()実装は、定義されたプロパティに対してのみ機能します。
バグではありません。これが、Arrayコンストラクターが機能するように定義されている方法です。
MDCから:
Arrayコンストラクターで単一の数値パラメーターを指定する場合、配列の初期長を指定します。次のコードは、5つの要素の配列を作成します。
_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
_までのすべての要素に値の割り当てを暗黙的に実行します。したがって、.map()
関数は、Arrayコンストラクターと数値引数を使用して新しく構築された配列で呼び出された場合、何もしません(コールバックを呼び出しません)。
配列に値を簡単に入力するためにこれを実行している場合、ブラウザのサポート上の理由で fill を使用できず、実際にforループを実行したくない場合は、x = new Array(3).join(".").split(".").map(...
これは空の文字列の配列を提供します。
かなりいですが、少なくとも問題と意図ははっきりと伝えられています。
回避策として、簡単なユーティリティメソッドを次に示します。
単純な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]