js에서 for의 index를 let로 선언했을 때 setTimeout을 실행했을 때의 트랜스 파일 결과를 조사해 보았다.

소개



es5까지는 var에서만 변수를 선언할 수 있었기 때문에 블록 단위로 변수를 선언할 수 없었습니다.

따라서 다음과 같은 일이 발생합니다.

es5.js
for (var i = 0; i < 10; i++) {
  setTimeout(() => console.log(i), 1000)
}

/* 結果
(1秒待つ)
10
10
10
10
10
10
10
10
10
10
*/

setTimeout의 람다 식에서 i는 클로저로 for i를 읽습니다.
  • setTimeout은 1 초 후에 i 값을 읽습니다
  • i는 var로 선언되었으므로 for 블록을 빠져도 선언되었습니다

  • 이상의 이유로부터, for문이 끝난 i=10의 상태로 1초 후에 일제히 표준 출력된다고 하는 느낌이군요

    그런데 let, const가 추가되어 블록 단위로 변수를 선언할 수 있게 되었습니다.

    for문의 index의 선언에 관해서도 for의 블록 단위의 스코프가 됩니다.
    setTimeout 과 같은 비동기라면 다음과 같은 결과가 됩니다.

    es2015.js
    for (let i = 0; i < 10; i++) {
      setTimeout(() => console.log(i), 1000)
    }
    
    /* 結果
    (1秒待つ)
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    */
    

    그러나 js에 익숙해지면 이 거동에 조금 위화감을 느꼈기 때문에 조금 조사해 보기로 했습니다.
    다만, js의 구현을 보는 것도 귀찮기 때문에 babel과 typescript로 트랜스파일 해 보았습니다.

    babel



    공식 사이트에서 변환을 시도 할 수 있으므로 위의 코드를 입력해 보았습니다.

    전송 전.js
    for (let i = 0; i < 10; i++) {
      setTimeout(() => console.log(i), 1000)
    }
    

    전송 후 .js
    "use strict";
    
    var _loop = function _loop(i) {
      setTimeout(function () {
        return console.log(i);
      }, 1000);
    };
    
    for (var i = 0; i < 10; i++) {
      _loop(i);
    }
    

    여기가 babel에서 전송의 결과입니다.
    과연 함수에 값을 전달하고 setTimeout을 실행하고 있습니다.

    좀 더 살펴보면, 아래와 같은 for문에 i를 클로저로써 람다 식에 건네주면 함수로서 전개되는 것 같습니다.

    전송 전.js
    for (let i = 0; i < 10; i++) {
      let func = () => i
      console.log(func())
    }
    

    전송 후 .js
    "use strict";
    
    var _loop = function _loop(i) {
      var func = function func() {
        return i;
      };
      console.log(func());
    };
    
    for (var i = 0; i < 10; i++) {
      _loop(i);
    }
    

    typescript



    typescript에서도 비슷한 (또는 거의 같은) 결과를 얻었습니다.

    전송 전.ts
    for (let i = 0; i < 10; i++) {
      setTimeout(() => console.log(i), 1000)
    }
    

    전송 후 .js
    var _loop_1 = function (i) {
        setTimeout(function () { return console.log(i); }, 1000);
    };
    for (var i = 0; i < 10; i++) {
        _loop_1(i);
    }
    

    이쪽의 결과도 거의 변하지 않았습니다.

    전송 전.ts
    for (let i = 0; i < 10; i++) {
      let func = () => i
      console.log(func())
    }
    

    전송 후 .js
    var _loop_1 = function (i) {
        var func = function () { return i; };
        console.log(func());
    };
    for (var i = 0; i < 10; i++) {
        _loop_1(i);
    }
    

    후기



    이전 Effective JavaScript을 읽었을 때, for문으로 비동기를 실행할 때는 즉시 실행 함수 안에 변수를 선언하는 방법이 실려 있었지만, 그 의미가 겨우 알았던 생각이 듭니다.

    js의 사양은 알수록 다른 언어와 다르며 안쪽이 깊고 재미 있습니다

    좋은 웹페이지 즐겨찾기