빌드노트
Array(3).map으로 만든 목록에 항목이 없는 이유
Array(3)과 명시적 undefined 배열의 콜백 호출 수·자체 속성 존재를 비교한다. length와 JSON 로그만으로는 빈 슬롯을 알아볼 수 없는 이유를 확인하고, Array.from의 생성 콜백으로 세 항목을 만든다.
Array(3).map((_, i) => "item-" + (i + 1))로 목록을 만들었는데 항목이 하나도 생기지 않을 수 있다. Array(3)은 길이만 3으로 정하고, map이 방문할 요소는 만들지 않기 때문이다. 개수만큼 항목을 만들 목적이라면 Array.from의 생성 콜백에서 값을 채우면 된다.
length가 3인데 콜백은 0번
Array(3)과 [undefined, undefined, undefined]의 차이는 값이 무엇인가보다 해당 인덱스가 존재하는가에 있다. 두 배열에 같은 콜백을 적용해 첫 칸의 자체 속성 존재 여부와 실제 호출 수를 확인해 보자. 별도 패키지 없이 Node.js에서 실행할 수 있다. 아래 코드를 새 holes.cjs 파일에 저장하고 node holes.cjs로 실행한다. 출력은 Windows의 Node.js 22.23.2에서 확인했으며, 프로토타입에 숫자 키를 추가하지 않은 조건이다.
const holes = Array(3);
const explicit = [undefined, undefined, undefined];
let calls = 0;
const toItem = (_, i) => {
calls++;
return `item-${i + 1}`;
};
const holesResult = holes.map(toItem);
console.log(holes.length, Object.hasOwn(holes, 0), calls);
calls = 0;
const explicitResult = explicit.map(toItem);
console.log(explicit.length, Object.hasOwn(explicit, 0), calls);
console.log(JSON.stringify(explicitResult));
앞의 두 줄은 각각 원본 길이, 인덱스 0의 자체 속성 존재 여부, map 콜백 호출 수다.
3 false 0
3 true 3
["item-1","item-2","item-3"]
Array(3)에는 인덱스 0, 1, 2의 자체 속성이 없다. 이런 자리를 빈 슬롯(hole)이라고 부른다. 첫 줄의 false와 호출 수 0이 이 예제의 원인을 보여 준다. holesResult도 길이는 3이고 세 자리가 빈 슬롯으로 남는다. 길이 0인 배열이 반환된 것은 아니므로 결과의 length만 검사하면 항목이 만들어졌다고 오해할 수 있다.
반면 explicit에는 값이 undefined인 요소 세 개가 존재한다. 콜백이 세 번 실행되면서 반환한 item-1부터 item-3까지의 문자열이 explicitResult에 들어갔다. 콜백이 값을 쓰지 않고 인덱스만 사용하더라도 빈 슬롯을 방문하게 되는 것은 아니다.
ECMAScript의 Array 생성 규칙 (새 탭에서 열림)은 숫자 인자 하나를 받으면 그 길이를 설정한다. map의 처리 규칙 (새 탭에서 열림)은 인덱스의 속성이 존재할 때만 콜백을 호출한다. 자기 코드에서도 문제가 생긴 원본의 Object.hasOwn(items, 0)과 콜백 호출 수를 함께 확인한다. 첫 칸의 검사만으로 배열 전체를 판단하지 말고, 호출 수나 방문 인덱스로 어느 자리가 처리됐는지 확인해야 한다. 호출이 정상인데 목록이 없다면 콜백 반환값과 그 결과를 소비하는 코드를 조사한다.
여기서 Object.hasOwn은 배열 자체의 속성만 검사한다. map이 사용하는 HasProperty 검사 (새 탭에서 열림)는 상속된 속성도 포함한다. 프로토타입에 숫자 키가 있는 환경에서는 자체 속성이 없다는 사실만으로 콜백이 실행되지 않는다고 단정할 수 없다.
undefined 조회와 JSON 로그가 숨기는 차이
첫 칸의 값을 읽는 것만으로는 두 배열을 구별할 수 없다. 같은 파일 아래에 다음 코드를 붙여 값 조회와 기본 JSON 로그를 비교한다.
console.log(holes[0], explicit[0]);
console.log(JSON.stringify(holes), JSON.stringify(explicit));
undefined undefined
[null,null,null] [null,null,null]
앞 배열은 인덱스가 없어서 조회 결과가 undefined이고, 뒤 배열은 존재하는 요소의 값이 undefined다. JSON 로그에서는 이 차이마저 사라진다. 여기서는 replacer나 toJSON을 추가하지 않았고, 상속된 숫자 키도 없다. 이 조건에서는 두 배열 모두 JSON 배열 직렬화 규칙 (새 탭에서 열림)에 따라 같은 null 표기로 바뀐다. 원래 배열에 null 세 개가 들어 있었다는 뜻은 아니다. 슬롯을 진단할 때는 JSON으로 바꾸기 전 원본의 속성과 콜백 호출을 확인해야 한다.
개수만큼 만들려면 Array.from에서 값을 채운다
빈 슬롯 배열을 만든 뒤 map으로 채우려던 식을 다음 생성식으로 바꾼다. 같은 파일 아래에 추가하면 실제로 만들어진 세 항목을 확인할 수 있다.
const items = Array.from({ length: 3 }, (_, i) => `item-${i + 1}`);
console.log(JSON.stringify(items));
["item-1","item-2","item-3"]
{ length: 3 }은 길이를 가진 일반 객체다. Array.from의 배열 유사 객체 처리 (새 탭에서 열림)는 이 길이만큼 각 인덱스를 읽고 생성 콜백의 반환값을 결과에 저장한다. 이 입력에서 첫 인자는 매번 undefined이므로 _로 두고, 두 번째 인자인 인덱스로 항목을 만들었다. 생성 콜백은 인덱스 0, 1, 2에서 실행되고, 결과에는 빈 슬롯 대신 문자열 세 개가 들어간다. 필요한 수정은 이 생성식이며, 확인을 마치면 호출 카운터와 출력 코드는 빼도 된다.
위 비교는 새 Node.js 프로세스에서 합성 배열만 실행한 결과다. 문서 설명은 2026년 10월 23일에 확인한 TC39의 ECMAScript 2027 초안에 근거한다. 브라우저나 UI 프레임워크의 실제 목록 렌더링은 실행하지 않았으며, 확인한 범위는 배열 생성·슬롯 존재·콜백 호출과 반환값이다.