ts, are you sure?

어제, 페이스북의 그룹에서 흥미로운 질문을 하나 보았었다.

funny

처음에는 오브젝트를 배열의 요소로 알아듣고,

길이가 8, 요소가 8개. 같은 의미입니다.

라고 답변했었는데, 작성자분이 올려주신 사진에는 전혀 예상치 못했던 출력이 나와 있었다.

what..????

오, 세상에. 같은 코드에서 다른 출력이라니. 아무리 자바스크립트가 멍청해도 이 정도는 아닐 거라 생각했는데.

잠시동안 당황했지만, 이내 정신을 차리고 먼저 타입스크립트가 어떻게 문제의 코드를 트랜스파일링을 하는 지 부터 tsc 를 통해 확인했다.

1
2
3
4
//JS code
[...Array(8)].map(_ => {name: ''});
//when tsc transplie this
Array(8).slice().map(function(_) { return {name: ''}; ));

의미상, Array(8) 은 new Array(8)과 일치합니다.

Vanilla JS와 TS의 차이는 크게 두 가지가 있었다.

  1. 바닐라의 경우 화살표 함수를 사용했지만, TS는 익명 함수 표현식으로 변경되었다.
  2. 바닐라의 spread operator가 TS에서는 Array.prototype.slice 메서드로 대체되었다.

1번이 내는 가시적인 차이점은 this와 arguments 바인딩 정도인데, 위 코드에서 두 요소를 사용하지 않았으므로 넘겼다.
즉, 2번이 문제라는 이야기인데, 구체적으로 무엇이 문제일까?

잠시 생각에 잠겨 있다, empty element 가 뇌리에 불현듯 스쳤다.

만약, Array.prototype.values (= Array.prototype[Symbol.iterator]) 이 존재하지 않는 요소를 복사하려 시도해 undefined를 그 값으로 얻고
spread operator 가 받아 결과로 내놓는 배열에 정의한다면? 그렇게 되어서 empty element 가 8개인 배열이 아닌 undefined 가 8개인 배열이 된다면?

이거다! 싶었었던 필자는 바로 명세서를 열어 둘의 알고리즘을 살피기 시작했다.

역시나, 둘의 알고리즘은 상이했다. 대략적으로 필자가 간추려서 차이점을 적어보았다.

  • slice
  1. 배열의 length 프로퍼티를 통해 길이를 얻는다.
  2. 길이를 기반으로 배열에서 얻을(slice) 길이를 구한다.
  3. 구한 길이와 시작 인덱스를 기반으로 루프를 돌며 아래의 알고리즘을 진행한다.
  4. 인덱스 i(임의의 변수, 통상적인 for 문의 i 를 생각하셔도 무방합니다.) 라는 이름의 프로퍼티가 배열에 없으면 넘어가고(continue), 있으면 그 값을 얻은 후 새로운 배열에 i 라는 이름과 함께 프로퍼티를 정의한다.
  5. 새로운 배열을 반환한다.
  • spread operator
  1. 배열의 length 프로퍼티를 통해 길이를 얻는다.
  2. 얻은 길이가 인덱스(임의의 변수, 통상적인 for 문의 i 를 생각하셔도 무방합니다.) 와 같아질 때 까지 아래의 알고리즘을 반복한다.
  3. 배열에서 i라는 이름의 프로퍼티를 가져옵니다. (이때, 존재하지 않으면 undefined를 그 값으로 얻게 됩니다.)
  4. 가져온 프로퍼티의 값을 i라는 이름으로 새로운 배열에 정의한다.
  5. 새로운 배열을 반환한다.

마지막으로 요약을 하자면,
Array.prototype.slice 는 존재하지 않는 요소를 복사하지 않지만, spread operator는 복사하여 결과 배열에 정의하기에 이후 실제 존재하는 배열의 요소만 순회하는 Array.prototype.map 의 동작의 영향을 준 것.
이라고 할 수 있다.

그럼, 이상으로 글을 마치도록 하겠다.


새벽에 갑자기 일어나서 써서 그런지 알고리즘 설명이 꼬여있는 거 같은 건 기분탓인가요..ㅜㅜ
존대가 아닌 어투로 글을 처음 써보았는데요, 제 평소 이미지랑 너무 안맞아서 그런지 어색합니다 ㅎㅎ

다음 글 부터는 다시 존대로 돌아가도록 하겠습니다!
읽어주셔서 감사합니다:)

공유하기