JavaScript "new Array (n)"및 "Array.prototype.map"기묘함
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);
정의되지 않은 포인터로 배열을 만듭니다.
두 번째는 정의되지 않은 3 개의 객체에 대한 포인터가있는 배열을 만듭니다.이 경우 포인터 자체는 정의되지 않았으며 자신이 가리키는 객체 만 정의됩니다.
y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];
배열이 객체의 컨텍스트에서 맵을 실행함에 따라 첫 번째 맵이 함수를 전혀 실행하지 못하는 반면 두 번째 맵은 실행되지 않습니다.
배열의 길이 만 알고 항목을 변환 해야하는 작업이있었습니다. 나는 이런 식으로하고 싶었다 :
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));
ES6을 사용하면 [...Array(10)].map((a, b) => a)
빠르고 쉽게 할 수 있습니다!
ES6 솔루션 :
[...Array(10)]
그러나 typescript (2.3)에서는 작동하지 않습니다.
배열이 다릅니다. 차이점은 new Array(3)
길이는 3이지만 속성 [undefined, undefined, undefined]
은없는 배열 을 만드는 반면 "0", "1"및 "2"라는 길이는 3 및 3 개의 속성을 가진 배열 을 만드는 것입니다 undefined
. in
연산자를 사용하여 차이점을 볼 수 있습니다 .
"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true
이것은 JavaScript에서 네이티브 객체의 존재하지 않는 속성 값을 얻으 undefined
려고하면 존재하지 않는 변수를 참조하려고 할 때 오류가 발생하지 않고 반환된다는 약간 혼란스러운 사실에서 비롯 됩니다 )는 속성이 이전에 명시 적으로로 설정된 경우와 동일합니다 undefined
.
MDC 페이지에서 map
:
[...]
callback
는 값이 할당 된 배열의 인덱스에 대해서만 호출됩니다. [...]
[undefined]
실제로 인덱스 있도록 (들)에 세터를 적용 map
하는 반면, 반복됩니다 new Array(1)
단지가 기본값으로 인덱스 (들)을 초기화 undefined
때문에 map
건너 뛰고 그것.
나는 이것이 모든 반복 방법에 대해 동일하다고 믿는다 .
I think the best way to explain this is to look at the way that Chrome handles it.
>>> x = new Array(3)
[]
>>> x.length
3
So what is actually happening is that new Array() is returning an empty array that has a length of 3, but no values. Therefore, when you run x.map
on a technically empty array, there is nothing to be set.
Firefox just 'fills in' those empty slots with undefined
even though it has no values.
I don't think this is explicitly a bug, just a poor way of representing what is going on. I suppose Chrome's is "more correct" because it shows that there isn't actually anything in the array.
In ECMAScript 6th edition specification.
new Array(3)
only define property length
and do not define index properties like {length: 3}
. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array-len Step 9.
[undefined, undefined, undefined]
will define index properties and length property like {0: undefined, 1: undefined, 2: undefined, length: 3}
. see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-runtime-semantics-arrayaccumulation ElementList
Step 5.
methods map
, every
, some
, forEach
, slice
, reduce
, reduceRight
, filter
of Array will check the index property by HasProperty
internal method, so new Array(3).map(v => 1)
will not invoke the callback.
for more detail, see https://www.ecma-international.org/ecma-262/6.0/index.html#sec-array.prototype.map
How to fix?
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);
Just ran into this. It sure would be convenient to be able to use Array(n).map
.
Array(3)
yields roughly {length: 3}
[undefined, undefined, undefined]
creates the numbered properties:
{0: undefined, 1: undefined, 2: undefined, length: 3}
.
The map() implementation only acts on defined properties.
Not a bug. That's how the Array constructor is defined to work.
From MDC:
When you specify a single numeric parameter with the Array constructor, you specify the initial length of the array. The following code creates an array of five elements:
var billingMethod = new Array(5);
The behavior of the Array constructor depends on whether the single parameter is a number.
The .map()
method only includes in the iteration elements of the array that have explicitly had values assigned. Even an explicit assignment of undefined
will cause a value to be considered eligible for inclusion in the iteration. That seems odd, but it's essentially the difference between an explicit undefined
property on an object and a missing property:
var x = { }, y = { z: undefined };
if (x.z === y.z) // true
The object x
does not have a property called "z", and the object y
does. However, in both cases it appears that the "value" of the property is undefined
. In an array, the situation is similar: the value of length
does implicitly perform a value assignment to all the elements from zero through length - 1
. The .map()
function therefore won't do anything (won't call the callback) when called on an array newly constructed with the Array constructor and a numeric argument.
If you are doing this in order to easily fill up an array with values, can't use fill for browser support reasons and really don't want to do a for-loop, you can also do x = new Array(3).join(".").split(".").map(...
which will give you an array of empty strings.
Quite ugly I have to say, but at least the problem and intention are quite clearly communicated.
Here's a simple utility method as a workaround:
Simple 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]
Complete Example
Here's a more complete example (with sanity checks) which also allows specifying an optional starting index:
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]
Counting Down
Manipulating the index passed to the callback allows counting backwards:
var count = 3;
var arr = arrayUtil.mapFor(count, function (i) {
return count - 1 - i;
});
// arr = [2, 1, 0]
Since the question is why, this has to do with how JS was designed.
There are 2 main reasons I can think of to explain this behavior:
Performance: Given
x = 10000
andnew Array(x)
it is wise for the constructor to avoid looping from 0 to 10000 to fill the array withundefined
values.Implicitly "undefined": Give
a = [undefined, undefined]
andb = new Array(2)
,a[1]
andb[1]
will both returnundefined
, buta[8]
andb[8]
will also returnundefined
even if they're out of range.
Ultimately, the notation empty x 3
is a shortcut to avoid setting and displaying a long list of undefined
values that are undefined
anyway because they are not declared explicitly.
Note: Given array a = [0]
and a[9] = 9
, console.log(a)
will return (10) [0, empty x 8, 9]
, filling the gap automatically by returning the difference between the two values declared explicitly.
In Chrome, if I do new Array(3)
I get []
, so my guess is that you've come across a browser bug.
참고URL : https://stackoverflow.com/questions/5501581/javascript-new-arrayn-and-array-prototype-map-weirdness
'Programming' 카테고리의 다른 글
명령 출력을 Makefile 변수에 지정하는 방법 (0) | 2020.05.14 |
---|---|
스토리 보드의 테이블 헤더 뷰 (0) | 2020.05.14 |
Django South-테이블이 이미 존재합니다 (0) | 2020.05.14 |
익명의 재귀 PHP 함수 (0) | 2020.05.14 |
CSS 센터 디스플레이 인라인 블록? (0) | 2020.05.14 |