3.4.1 Array()

jude·2022년 2월 4일
0

you don't know js

목록 보기
25/30
post-thumbnail

생성자로 배열을 선언하거나 리터럴로 배열을 생성하거나 동일하다.

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가지 방법으로 살펴보자

  • new Array(3) 케이스 => [비어 있음 x 3]
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, "-") ); // '--'
  • 값이 있는 리터럴 케이스 => [1, 2, 3]
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} 이런식으로 쓰지 말라는 것이다.

배열 생성할 때는 그냥 리터럴을 쓰자!

profile
UI 화면 만드는걸 좋아하는 UI개발자입니다. 프론트엔드 개발 공부 중입니다. 공부한 부분을 블로그로 간략히 정리하는 편입니다.

0개의 댓글