JavaScript“new Array (n)”및“Array.prototype.map”기묘함


209

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)있습니까?


배열 리터럴 표기법과 동일한 결과를 얻지 못했습니다. 나는 여전히 0 대신에 undefined를 얻는다. 만약 내가 뭔가를 설정하면 0 개의 결과 만 얻을 것이고 var y = x.map(function(){return 0; });, 새로운 Array () 메소드와 배열 리터럴 둘 다에 대해 이것을 얻는다. Firefox 4와 Chrome에서 테스트했습니다.
RussellUresti

Chrome에서 버스 팅 된 경우 언어로 정의되어있을 수 있지만 말이되지 않기 때문에 실제로는 희망이 없습니다.
Hashbrown

답변:


125

첫 번째 예는

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 = [,,,];

배열이 객체의 컨텍스트에서 맵을 실행함에 따라 첫 번째 맵이 함수를 전혀 실행하지 못하는 반면 두 번째 맵은 실행되지 않습니다.


86
에서 MDC (강조 광산) " map순서로 배열의 각 요소에 대해 한번 제공 콜백 함수를 호출하고, 그 결과로부터 새로운 배열을 구성한다. callback유일한 값을 할당 배열의 인덱스에 대해 호출 그것은 호출되지 삭제되었거나 값이 할당되지 않은 색인의 경우 " 이 경우 x의 값은 명시 적으로 값을 할당하지 않은 반면 값은 값이더라도 y할당되었습니다 undefined.
Martijn

2
따라서 JavaScript가 정의되지 않은 포인터인지 또는 정의되지 않은 포인터인지 확인할 수 없다는 것이 JavaScript입니까? 내 말은 (new Array(1))[0] === [undefined][0].
Trevor Norris

정의되지 않은 배열은 정의되지 않은 객체에 대한 포인터 배열과 다릅니다. undefines의 배열은 null 값의 배열 [null, null, null]과 같지만 undefined에 대한 포인터 배열은 [343423, 343424, 343425]의 null 및 null 및 null에 대한 위치와 같습니다. 두 번째 솔루션에는 메모리 주소를 가리키는 실제 포인터가 있지만 첫 번째 솔루션은 어디에도 가리 키지 않습니다. 그 JS의 실패 아마 토론 오 문제는 아니지만 여기 경우)
데이비드 Mårtensson은

4
@TrevNorris, 버그가 hasOwnProperty없는 한 쉽게 테스트 할 수 있습니다 hasOwnProperty: (new Array(1)).hasOwnProperty(0) === false[undefined].hasOwnProperty(0) === true. 사실, 당신과 동일한 작업을 수행 할 수 있습니다 in: 0 in [undefined] === true0 in new Array(0) === false.
squid314

3
JavaScript에서 "정의되지 않은 포인터"에 대해 이야기하면 문제가 혼동됩니다. 찾고있는 용어는 "elisions" 입니다. x = new Array(3);x = [,,,];같지 않습니다 x = [undefined, undefined, undefined].
Matt Kantor

118

배열의 길이 만 알고 항목을 변환 해야하는 작업이있었습니다. 나는 이런 식으로하고 싶었다 :

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);

최신 정보

다른 해결책은 다음과 같습니다.

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

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


28
당신을 주목할만한 상태에 필요하지 않은 undefined.fill()아주 약간의 코드를 단순화 방법let arr = new Array(10).fill().map((val,idx) => idx);
얀 이브

마찬가지로Array.from(Array(10))

84

ES6을 사용하면 [...Array(10)].map((a, b) => a)빠르고 쉽게 할 수 있습니다!


9
Pre-ES6을 사용할 수 있습니다 new Array(10).fill(). [...Array(10)]
Molomby

대규모 배열의 경우 확산 구문으로 인해 문제가 발생하므로 피하는 것이 좋습니다.

또는[...Array(10).keys()]
Chungzuwalla

25

ES6 솔루션 :

[...Array(10)]

그러나 typescript (2.3)에서는 작동하지 않습니다.


6
Array(10).fill("").map( ...Typescript 2.9와 함께 나를 위해 일한 것입니다
ibex

19

배열이 다릅니다. 차이점은 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.


17

MDC 페이지에서 map:

[...] callback는 값이 할당 된 배열의 인덱스에 대해서만 호출됩니다. [...]

[undefined]실제로 인덱스 있도록 (들)에 세터를 적용 map하는 반면, 반복됩니다 new Array(1)단지가 기본값으로 인덱스 (들)을 초기화 undefined때문에 map건너 뛰고 그것.

나는 이것이 모든 반복 방법에 대해 동일하다고 믿는다 .


8

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, 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);

아주 좋은 설명입니다.
Dheeraj Rao

7

이것을 설명하는 가장 좋은 방법은 Chrome이 처리하는 방식을 보는 것입니다.

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

실제로 실제로 일어나는 일은 new Array ()가 길이가 3이지만 값이없는 빈 배열을 반환한다는 것입니다. 실행할 때 따라서 x.mapA의 기술적 빈 배열로 설정 될 것은 없다.

Firefox undefined는 값이 없어도 빈 슬롯을 '채 웁니다' .

나는 이것이 분명히 버그라고 생각하지 않으며, 진행중인 일을 표현하는 나쁜 방법 일뿐입니다. Chrome의 배열이 실제로 없다는 것을 보여주기 때문에 Chrome이 더 정확하다고 가정합니다.


4

이것에 부딪쳤다. 사용하는 것이 편리 할 것 Array(n).map입니다.

Array(3) 대략 수율 {length: 3}

[undefined, undefined, undefined]번호가 매겨진 속성을 만듭니다
{0: undefined, 1: undefined, 2: undefined, length: 3}.

map () 구현은 정의 된 속성에서만 작동합니다.


3

버그가 아닙니다. 이것이 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. 배열에서 상황은 비슷합니다.의 값은 length0에서까지의 모든 요소에 값을 암시 적으로 수행합니다 length - 1. .map()기능 때문에 새로 Array 생성자와 숫자 인수로 구성라는 배열에 아무 것도 (콜백을 호출하지 않습니다)하지 않습니다.


깨진 것으로 정의 되었습니까? 그것은 항상 undefined영원히 있을 세 가지 요소의 배열을 생성하도록 설계 되었습니까?
궤도에서 가벼움 경주

"영원한"부분을 제외하고는 맞습니다. 나중에 요소에 값을 할당 할 수 있습니다.
Pointy

3
그것이 x = []대신 사용해야 합니다.x = new Array()
Rocket Hazmat

3

값으로 배열을 쉽게 채우기 위해이 작업을 수행하고 브라우저 지원 이유로 채우기 를 사용할 수 없으며 실제로 for 루프를 원하지 않는 경우 x = new Array(3).join(".").split(".").map(...빈 배열을 제공 할 수도 있습니다 문자열.

아주 못생긴 말이지 만 적어도 문제와 의도는 분명하게 전달됩니다.


1

문제는 이유이기 때문에 이것은 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] = 9, console.log(a)반환 (10) [0, empty x 8, 9]두 값의 차이를 반환하여 자동으로 간격을 채우는는 명시 적으로 선언했다.


1

다른 답변에 철저히 설명 된 이유로 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


0

해결 방법으로 간단한 유틸리티 방법이 있습니다.

간단한지도

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]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.