221201 항해99 25일차 this 바인딩

요니링 컴터 공부즁·2022년 12월 1일
0

this 바인딩

  • 기본 바인딩
  • 암시적 바인딩
  • 명시적 바인딩
  • new 바인딩
  • 화살표 함수(ES6)

기본 바인딩(Default Binding)

  • 기본 바인딩이 적용될 경우 this는 전역 객체에 바인딩된다.(브라우저 환경인 경우 window, Node.js 환경인 경우 global)
function foo() {
  const a = 10;
  console.log(this.a);
}

foo(); // undefined
  • 위와 같은 경우 this는 전역객체에 바인딩되고 전역객체에는 a라는 프로퍼티가 없기 때문에 undefined가 출력된다. 전역객체에 a라는 프로퍼티가 있는 경우 해당 a프로퍼티의 값을 출력하게 된다.
  • 하지만 엄격모드에서는 기본 바인딩 대상에서 전역객체는 제외된다. 전역객체를 참조해야할 this가 있다면 그 값은 undefined가 된다.

암시적 바인딩 (Implicit Binding)

  • 암시적 바인딩이란, 함수가 객체의 메서드로서 호출되는 상황에서 this가 바인딩되는 것을 말한다. 이때 this는 해당 함수를 호출한 객체, 즉 콘텍스트 객체에 바인딩된다.
const foo = {
  a: 20,
  bar: function () {
    console.log(this.a);
  }
}

foo.bar(); // 20
  • 암시적 바인딩을 사용할 때 발생할 수 있는 문제는 위와 같은 상황에서 함수를 매개변수(콜백)로 넘겨서 실행하는 것이다. 이런 상황에선 함수의 콘텍스트를 가지고 있지 않는 레퍼런스일 뿐이기 때문에 암시적 바인딩이 소실된다.

명시적 바인딩 (Explicit Binding)

  • 자바스크립트의 모든 Function 은 call(), apply(), bind()라는 프로토타입 메소드를 가지고있다. 이 3가지 메서드 중 하나를 호출함으로써 this 바인딩을 코드에서 명시하는 것을 명시적 바인딩이라고 한다. 이때 this는 내가 명시한 객체에 바인딩된다.
const foo = {
  a: 20,
}

function bar() {
  console.log(this.a);
}

bar.call(foo); // 20
bar.apply(foo); // 20
  • bar의 Function 프로토타입 메서드 call, apply의 매개변수로 바인딩할 객체를 넘겨주면서 bar 함수를 실행할 때의 this 컨텍스트를 foo로 직접 바인딩 해주었다.
  • call과 apply의 동작은 같지만 두번째 매개변수로 객체의 인자를 전달해주는데(e.g. 생성자의 매개변수), call은 매개변수의 목록, apply는 배열을 받는다는 차이점이 있다.
const foo = {
  a: 20,
}

function bar() {
  console.log(this.a);
}

const bound = bar.bind(foo)

bound(); // 20
  • bind 메서드는 매개변수로 전달받은 오브젝트로 this가 바인딩된 함수를 반환한다. 이것을 하드 바인딩이라고 하는데 하드 바인딩된 함수는 이후 호출될 때마다 처음 정해진 this 바인딩을 가지고 호출된다.

new 바인딩 (new Binding)

  • 자바스크립트의 new 키워드는 함수를 호출할 때 앞에 new 키워드를 사용하는 것으로 객체를 초기화할 때 사용하는데, 이때 사용되는 함수를 생성자 함수라고 한다.(컨벤션으로 생성자 함수는 대문자로 시작한다)
  • 그리고 생성자 함수에서는 this키워드를 해당 생성자를 이용해 생성할 객체에 대한 참조로 사용한다.
function Foo() {
  this.a = 20;
}

const foo = new Foo();

console.log(foo.a); // 20
  • 위 코드에서 Foo 함수가 new 키워드와 함께 호출되는 순간 새로운 객체가 생성되고, 새로 생성된 객체가 this로 바인딩이 된다. 그리고 생성된 객체의 a라는 프로퍼티에 20이라는 값이 할당되고, 해당 객체는 foo라는 변수에 할당된다.

화살표 함수

  • ES6에 추가된 화살표 함수(Arrow Function)는 this를 바인딩할 때 앞서 설명한 규칙들이 적용되지 않고, this에 어휘적 스코프(Lexical scope)가 적용된다.즉, 화살표 함수를 정의하는 시점의 컨텍스트 객체가 this에 바인딩된다.
const foo = {
  a: 20,
  bar: function () {
    setTimeout(() => {
      console.log(this.a);
    }, 1);
  }
}

foo.bar(); // 20
  • setTimeout의 콜백인 화살표 함수의 선언시에 this는 foo 객체를 가리키고 있기 때문에(렉시컬 스코프), 콜백이 실행될 때 this는 foo를 가리키게 된다.

  • 화살표 함수로 선언시에 렉시컬 스코프를 통해 바인딩된 this는 apply, bind등의 함수나 new 함수로 오버라이드할 수 없다. 그렇기때문에 주로 콜백 함수로 사용할 때 유용하다.

  • apply, call, bind같은 바인딩 메소드, 또는 화살표 함수가 나오기 전에는 골치아픈 this 바인딩을 _this, that, self와 같은 렉시컬 스코프를 이용해서 해결하였다. 호출시 결정되는 this를 렉시컬 스코프를 이용해 선언시에 정해주는 효과를 주는 것이다.

~렉시컬 스코프란?
렉시컬 스코프란 중첩된 함수 그룹에서 내부 함수가 상위 범위의 변수 및 기타 리소스에 액세스 할 수 있음을 의미한다.
즉, 함수를 어디서 선언하였는지에 따라 상위 스코프를 결정한다는 뜻이며, 가장 중요한 점은 함수를 어디서 호출하는지가 아니라 어디에 선언하였는지에 따라 결정된다는 것이다.
다른 말로, 정적 스코프(Static Scope)라 부르기도 한다.

var x=1;

function foo() {
  var x=10;
  bar();
}

function bar() {
  console.log(x);
}

foo(); //1
bar(); //1

위 예제를 보듯 JS는 렉시컬 스코프를 따르기에 함수를 선언한 시점에 상위 스코프가 결정된다. 함수를 어디서 호출했는지는 스코프 결정에 아무런 영향을 주지 않는다. 위 예제의 bar 함수는 전역에 선언됐기에 bar 함수의 상위 스코프는 전역 스코프이고, 따라서 전역 변수 x의 값 1을 두번 출력한다.

참조:
[JS] 알쏭달쏭 자바스크립트 this 바인딩
[JS] 6. 함수 스코프 & 블록 스코프 & 렉시컬 스코프

0개의 댓글