생성자로 배열을 선언하거나 리터럴로 배열을 생성하거나 동일하다.
var a = new Array( 1, 2, 3 );
a; // [1, 2, 3]
var b = [1, 2, 3];
b; // [1, 2, 3]
아래와 같은 기능이 있으니, 되도록이면 배열은 리터럴로 생성하자.
var a = new Array(3); // 인자를 하나만 넣으면, 배열의 크기가 미리 정해진다.
console.log(a); // [비어 있음 x 3]
console.log(a.length); // 3
현재 크롬 버전 98에선 [비어 있음 x 3] 으로 나오지만, 버전마다 조금씩 차이가 있다. (ex. [undefined x 3] )
(그냥 리터럴을 쓰자;)
var a = new Array(3);
var b = [undefined, undefined, undefined];
var c = [];
c.length = 3;
console.log(a); // [비어 있음 x 3]
console.log(b); // [undefined, undefined, undefined]
console.log(c); // [비어 있음 x 3]
그런데 어떨 땐 같은 값처럼 보일 때가 있어서 조심해야 한다. (그냥 리터럴을 쓰자;)
var a = new Array(3);
var b = [undefined, undefined, undefined];
a.join('-'); // --
b.join('-'); // --
a.map((el, i) => i); // [비어 있음 x 3]
b.map((el, i) => i); // [0, 1, 2]
map() 은 순회할 요소 자체가 없기 때문에 요소의 비어 있음을 [비어 있음 x 3] 와 같이 표현한다.
join()은 왜 저런 결과가 나올까?
join()의 구현 로직을 2가지 방법으로 살펴보자
function fakeJoin(arr, connector) {
var str = '';
for (var i = 0; i < arr.length; i++) {
// 1. 배열[0] 요소를 건너 뛴다.
// 3. 빈 문자열 '' 에 연결자 '-'를 붙여 str에 할당 => '-'
// 5. '-' 에 연결자 '-'를 붙여 str에 다시 할당 => '--'
if (i > 0) {
str += connector;
}
// 2. 배열[0]이 undefined 이기 떄문에 조건문 패스
// 4. 배열[1]이 undefined 이기 때문에 조건문 패스
// 6. 배열[2]이 undefined 이기 때문에 조건문 패스
if (arr[i] !== undefined) {
console.log('test');
str += arr[i];
}
// 7. for문 done;
}
return str; // 8. '--' 리턴
}
var a = new Array(3); // [비어 있음 x 3]
console.log(fakeJoin(a, "-") ); // '--'
function fakeJoin(arr, connector) {
var str = '';
for (var i = 0; i < arr.length; i++) {
// 1. 배열[0] 요소를 건너 뛴다.
// 4. '1'에 연결자 '-'를 붙여서 str에 다시 할당 => '1-'
// 6. str('1-2')에 연결자 '-'를 붙여서 str에 다시 할당 => '1-2-'
if (i > 0) {
str = str + connector;
}
// 2. (if문) undefined 가 아니고, 값이 있을 경우라 통과(추후 계속 통과)
// 3. str('')에 배열[0] 요소인 1을 붙이고 str에 할당 => '1'
// 5. str('1-') 에 배열[1] 요소인 2를 붙이고 str에 다시 할당 => '1-2'
// 7. str('1-2-') 에 배열[2] 요소인 3을 붙이고 str에 다시 할당 => '1-2-3'
if (arr[i] !== undefined) {
str = str + arr[i];
}
// 8. for문 done;
}
return str; // 9. str( '1-2-3' )을 리턴
}
var a = [1,2,3];
console.log( fakeJoin(a, "-") ); // '1-2-3'
join()은 배열의 length가 있다는 가정하에 순회한다.
map() 함수는 이러한 가정이 없기 때문에, 비어있는 배열이 입력되면 오작동 or 의도치 않은 결과가 발생한다.
(그냥 리터럴을 쓰자;)
비어있는 배열 말고 진짜 undefined로 구성된 배열 생성하는 방법
(var a = [undefined, undefined, undefined] 와 동일하긴 하지만, 아래는 하드코딩을 하지 않는 방법)
var a = Array.apply(null, {length: 3})
a; // [undefined, undefined, undefined]
a.map((el, i) => i); // [0, 1, 2]
- apply()는 모든 함수에서 사용 가능한 유틸리티로, call()과 기능상 같다,
- 첫번째 인자는, Array(apply 함수를 호출한)의 기능을 (빌려줄 객체) 인데, 여기선 그런거 없으니 null => 즉 Array 호출
- call() 은 2번째, 3번째, 4번째, ... 인자들로 Array 함수에 인자값을 전달하고
- apply() 는 2번째 인자에 전달한 인자들로 구성된 배열을 넣는다. apply 내부에서는 들어온 배열(혹은 유사 배열)을 펼쳐서 Array 함수의 인자로 전달한다.
즉, Array 함수에 {length: 3} 를 펼쳐서 인자로 넣는다.
apply() 의 내부는 배열(유사 배열)을 인자로 받아서 join() 구현 로직처럼 루프를 돌며 인자 하나 하나 순회할 것이다.
아래와 같이 객체에 length 프로퍼티를 넣으면 함수 내부에서는 arr.length에 접근 가능해져서 루프를 돌 것이다.
(오... 유레카!)
function test(arr) {
console.log( arr.length ) // 3
}
test({length: 3})
apply()는 arr[0], arr[1], arr[2] 이런식으로 접근하며 순회할테고 접근할 때마다 undefined를 반환하게 되어, 비어 있는 슬롯이 아닌 undefined로 채워진 배열이 반환된다.
책에서는 장황하고 이상한 케이스들을 여럿 보여줬지만, 중요한건 어쨌든 배열을 생성할 때 생성자나 apply() 를 통해 {length:3} 이런식으로 쓰지 말라는 것이다.
배열 생성할 때는 그냥 리터럴을 쓰자!