[Javascript] This Binding

피누·2019년 10월 24일
1

어제 만난 웨일 확장앱을 개발하면서 만난 이슈는 다음과 같다.

  • Class 내부의 콜백함수에서 클래스 내부 메소드를 호출하는데 this가 바인딩되지 않아 함수 호출이 이루어지지 않았다.
  • fetch API를 통해 blob url로 요청하니 CORS 이슈가 발생했다.

두가지 모두 구글의 도움으로 해결 할 수 있었으나, 아직 자바스크립트가 많이 부족함을 느꼈다. 앞으로 웨일 확장앱 개발을 진행하면서 만난 이슈의 기본 개념에 대해 포스팅을 작성하기로 결심했다. 우선 오늘은 첫번째 이슈였던 바인딩에 관해 알아보자

ES6의 화살표 함수는 this를 자체적으로 바인딩 하지 않는다. 때문에 화살표 함수를 사용하면 별도로 binding을 해주지 않고도 사용가능하다. 아래 예제에는 화살표 함수가 사용되어 포스팅 설명과 틀린 부분이 있을 수 있다. 양해 바란다.

바인딩이란?

바인딩(Binding)이란 함수 호출과 실제 함수를 연결하는 방법이다. 즉 함수를 호출하는 부분에 함수가 위치한 메모리 번지를 연결시켜 주는 것이다. 자바스크립트에서 함수를 호출 할 때는 암묵적으로 arguments 객체 및 this 변수가 함수 내부로 전달된다. 이에 따라 this에 할당되는 값이 달라지게 되어 많은 개발자들이 혼돈을 겪게 된다. 지금부터 하나씩 알아보자

This Binding Rule

1. 객체의 메서드 호출 할 때 this 바인딩

  • 객체 내부의 함수를 메소드라고 부른다. 메소드 내부 코드에서 사용된 this는 해당 메소드를 호출한 객체로 바인딩 된다.
var myObject = {
      name: 'foo',
      sayName: () => {
        console.log(this.name);
      }
}
var otherObject = {
      name: 'bar'
}
    
otherObject.sayName = myObject.sayName;

myObject.sayName(); // foo
otherObject.sayName(); // bar

2. 함수를 호출 할 때 this 바인딩

  • 함수를 호출할 경우는, 해당 함수 내부 코드에서 사용된 this는 전역 객체에 바인딩 된다. 브라우저에서 자바스크립트를 실행하는 경우 전역 객체는 window 객체다.
var test = 'This is test';
console.log(window.test); // 'This is test'

var sayFoo = () => {
  console.log(this.test);
}

sayFoo(); // 'This is test'
  • 이러한 특성은 내부 함수(inner fuction)을 호출 했을 경우에도 동일하게 적용된다.
var value = 100;

var myObject = {
  value : 1,
  func1 : () => {
    this.value += 1;
    console.log('func1() called. this.value : ' + this.value); // 2, 호출한 객체의 this가 바인딩
    // func1 내부 함수 func2
    func2 = () => {
      this.value +=1;
      console.log('func2() called. this.value : ' + this.value); // 101, 내부함수로서, 전역객체에 binding 된다.

      //func2의 내부 함수 func3
      func3 = () => {
        this.value += 1;
        console.log('func3() called. this.value : ' + this.value); // 102
      }
      func3();
    }
    func2();
  }
};

myObject.func1();

위 코드에서 내부 함수 호출 패턴을 정의해 놓지 않았기 때문에, 내부 함수들은 함수로 취급되어 this는 전역 객체에 바인딩 된다. 그래서 흔히들 that 변수를 이용해 this값을 저장한다. 아래와 같은 식이다.

var value = 100;

var myObject = {
  value : 1,
  func1 : () => {

    var that = this; // 현재 바인딩 된 this(myObject)를 that에 저장

    this.value += 1;
    console.log('func1() called. this.value : ' + this.value); // 2, 
    // func1 내부 함수 func2
    func2 = () => {
      that.value +=1;
      console.log('func2() called. this.value : ' + that.value); // 3
    }
    func2();
  }
};

myObject.func1();
  1. 생성자 함수를 호출 할 때 this 바인딩
  • 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작한다. 생성자 함수에서의 this는 생성자 함수를 통해 새로 생성되어 반환되는 개체에 바인딩된다.
  1. call과 apply 메소드를 이용한 명시적인 바인딩
  • Function.prototype 객체의 메서드인 call()apply()를 통해 명시적으로 this를 바인딩 가능하다. 기본 문법은 아래와 같다.
function.apply(thisArg, argArray)

call()apply()의 기능은 함수 호출이다. thisArg는 this에 바인딩 할 객체, argArray는 함수를 호출 할 때 넘길 인자들의 배열이다.

function Person(name, age){
  this.name = name;
  this.age = age;
}

var applyPerson = {};
var newPerson = new Person('mike', 24);

Person.apply(applyPerson, ['Jack', 24]);

console.log(applyPerson.name) // Jack
console.log(newPerson.name) // call

만난 이슈

whale.tabs.executeScript({
    code: `
    window.showSubtitle = new ${ShowSubtitle}();
    window.showSubtitle.getSubtitles();
  `
  }, function (result) { 

    if(result == null)
      console.log("자막을 가져오는데 오류가 발생했습니다.");
    else
      this.transSubtitles(result[0]);

  }.bind(this))
};
  • Class 내부의 콜백 함수중, class 메소드를 호출하는데 콜백함수에서는 전역객체가 바인딩되어 함수를 찾지 못하는 이슈가 있었다. Callback 함수에 현재 객체를 바인딩해줌으로서 해결 할 수 있었다.

Refer

http://jeonghwan-kim.github.io/2017/10/22/js-context-binding.html
https://preamtree.tistory.com/117

profile
어려운 문제를 함께 풀어가는 것을 좋아합니다.

3개의 댓글

comment-user-thumbnail
2021년 3월 14일

var myObject = {
name: 'foo',
sayName: () => {
console.log(this.name);
}
}
var otherObject = {
name: 'bar'
}

otherObject.sayName = myObject.sayName;

myObject.sayName(); // foo
otherObject.sayName(); // bar

이분분에서 각각 foo, bar가 아닌 undefined가 될것 샅아요 이유는 화살표함수는 렉시컬 바인딩을 하는데 객체 내부에서 바인딩을 보면 this는 윈도우기 때문입니다

1개의 답글