콜백 함수의 스코프(+화살표 함수, this)

김정준·2022년 9월 29일
1

JS

목록 보기
11/13

1. JS 함수에 관한 사실 몇 가지

1) 자바스크립트 엔진은 함수 선언문으로 함수를 정의하면 자바스크립트 엔진이 암묵적으로 함수 이름과 동일한 이름의 식별자를 생성하고 거기에 함수객체를 할당한다.

  function test(a,b){
    console.log(a,b)
  }
 
  // 위 코드를 의사 코드로 표현하면 아래와 같다
  var test = function test(a,b){
    console.log(a,b)
  }
console.log(out) // ① out 함수 출력

function out(){

  console.log(inside) // ③ ****** undefined ******

  {
    console.log(inside) // ④ ****** inside 함수 출력 ******
    function inside(){}
  }

  console.log(inside) // ⑤ inside 함수 출력

}

out() // ②
console.log(inside) // ⑥ inside is not defined

③번과 ④번 주의

  console.log(test); // undefined
  {
    console.log(test); // f test() {}
    function test() {}
  }
  console.log(test); // f test() {}
출처 : 이웅모, ⌜모던 자바스크립트 Deep Dive⌟, 위키북스, 2022, 162쪽

2) 익명 함수 리터럴콜백 함수고차 함수에 전달하면 익명 함수 리터럴고차함수호출할 때마다 평가되어 함수객체를 생성한다.
고차함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차함수에 전달하면 콜백 함수는 단 한번만 생성된다.

// 익명함수 리터럴은 test함수를 호출할 때마다 평가되어 함수 객체를 생성한다
test(function(){console.log(a)}

// insider 함수는 단 한번만 생성된다
var insider = function(){console.log(a)}
test(insider) // 고차 함수에 함수 참조 전달
출처 : 이웅모, ⌜모던 자바스크립트 Deep Dive⌟, 위키북스, 2022, 185쪽

3) 자바스크립트는 렉시컬 스코프를 따지므로 함수를 어디서 호출했는지가 아니라 함수를 어디서 정의했는지에 따라 상위 스코프를 결정한다. 함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다. 즉, 함수의 상위 스코프는 언제나 자신이 정의된 스코프다.

출처 : 이웅모, ⌜모던 자바스크립트 Deep Dive⌟, 위키북스, 2022, 199쪽

4) 화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다. 따라서 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다. 이를 lexical this라 한다. 이는 마치 렉시컬 스코프와 같이 화살표 함수의 this가 정의된 위치에 의 해 결정된다는 것을 의미한다.

출처 : 이웅모, ⌜모던 자바스크립트 Deep Dive⌟, 위키북스, 2022, 480쪽

2. 예제코드

  let a = 1;
  {
    let a = 2;
    function outer(콜백) {
      콜백(); // 4
      console.log(a); // 2
      // outer의 상위 스코프와 콜백의 상위 스코프가 다르다
    }
  }

  {
    a = 4; // ①
    outer(function(){console.log(a)}); // 이 콜백함수의 상위 스코프는 ① 이다
    // outer의 상위 스코프와 콜백함수의 상위 스코프가 다르다
    console.log(a); // 4
  }
 
  console.log(a); // 4
  console.log(window.a); // undefined
  let a = 1;

  {
    let a = 2;
    function outer(콜백) {
      콜백(); // 33
      console.log(a); // 2
    }
  }

  {
    a = 4;
    outer(function () {
      a = 33; // ①
      console.log(a);
    });
   
    console.log(a); // 33
  }

  console.log(a); // 33
  console.log(window.a) // undefined

① 을 let a = 33; 으로 바꾸면 33, 2, 4, 4, undefined가 차례로 출력된다.

  let a = 1;

  var test = function () {console.log(a)};

  {
    let a = 2;
    {
      a = 33;
      arrow = () => {console.log(a);};
      test2 = function () {console.log(a);};
    }
  }

  function outer(콜백) {
    let a = 4;
    콜백();
  }

  outer(test); // 1
  outer(arrow); // 33
  outer(test2); // 33

  outer(function () {console.log(a);}); // 1
 
  {
    let a = 222;
    outer(function(){console.log(a)}); // 222
    outer(test); // 1
  }

  outer(function(){console.log(a)}); // 1
  outer(test); // 1

화살표 함수 내부의 this

  a = 6;
 
  // 생성자 함수
  function test() {
    let a = 4;

    this.a = 1;
    this.sayHi = function () {

      let a = 5;
      console.log(this); 
      that = this;
     
      return function (콜백) {
        콜백();
        console.log(that);
        console.log(this);
        console.log(a);
      };
    };

    console.log(this);
  }

  obj = new test(); // test {a: 1, sayHi: ƒ}
  f = obj.sayHi(); // ① test {a: 1, sayHi: ƒ}
  console.log(f); // 반환된 함수

  f(() => {console.log(this)}); // 콜백함수(화살표함수)의 상위 스코프는 전역 스코프
  // window   
  // test {a: 1, sayHi: ƒ}   :   ① 에서 that에 대입된 this 
  // window   :   일반 함수 내부의 this
  // 5   :   함수의 스코프는 정의 위치에 따른 렉시컬 스코프에 따라 결정됨

  console.log(a); // 6

  obj.sayHi()(() => {console.log(this)});
  // test {a: 1, sayHi: ƒ}   :   sayHi()의 console.log(this)
  // window
  // test {a: 1, sayHi: ƒ}
  // window  
  // 5

  g = f;
  g(function(){console.log(this)});
  // window   :   콜백 함수가 일반 함수로 호출 되니까
  // test {a: 1, sayHi: ƒ}
  // window   :   일반 함수 내부의 this
  // 5

1개의 댓글

comment-user-thumbnail
2022년 9월 29일

와...

답글 달기