[Javascript] 함수호출방식에 따른 this 바인딩

Gyuhan Park·2022년 11월 12일
0

javascript deepdive

목록 보기
5/11

this

자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수.
this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있다. ex) this.name, this.getName 등

언제 this를 사용하나요?

메서드는 자신이 속한 객체의 프로퍼티를 참조하고 변경할 수 있어야 한다.
자신이 속한 객체의 프로퍼티를 참조하려면 자신이 속한 객체를 가리키는 식별자를 알아야 한다.

function Circle(radius) {
  ???.radius = radius;
}

// 객체가 생성할 인스턴스를 가리키는 식별자를 알 수 없다.
Circle.prototype.getDiameter = function() {
	return 2 * ???.radius;
}

But, 객체를 정의하는 시점에는 아직 인스턴스를 생성하기 이전이므로 객체가 생성할 인스턴스를 가리키는 식별자를 알 수 없다.

이때, this라는 특수한 식별자를 사용한다.

this 바인딩

식별자 역할을 하는 this와 this가 가리킬 객체를 바인딩 하는 것.
변수 선언을 했을 때 변수 이름과 확보된 메모리 공간의 주소를 연결하는 것처럼 this와 this가 가리킬 객체를 연결하는 것을 this 바인딩이라고 한다.

this 바인딩은 항상 같은 값을 가리키지 않고 함수 호출 방식에 따라 동적으로 결정된다.

함수호출방식에 따른 this 바인딩

  1. 일반 함수 호출 -> 전역 객체(window)
  2. 메서드 호출 -> 메서드를 호출한 객체
  3. 생성자 함수 호출 -> 인스턴스
  4. function.prototype.apply/call/bind 메서드에 의한 간접 호출 -> 메서드의 첫번째 인자

일반 함수 호출

전역 객체(window)가 this에 바인딩된다.
중첩 함수, 콜백 함수 등 어떤 함수라도 일반 함수로 호출되면 this에 전역 객체가 바인딩된다.

function foo() {
  console.log(this); // window
  function bar() {
    console.log(this); // window
  }
}

메서드 호출

메서드 내부의 this는 메서드를 호출한 객체에 바인딩된다.

const person = {
  name: 'Lee',
  getName() {
    return this.name;
  }
};

console.log(person.getName()); // Lee

생성자 함수 호출

생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다. 생성자 함수를 선언하고 인스턴스를 생성할 땐 new연산자를 사용한다. new연산자를 붙이지 않으면 일반 함수로 동작하여 this도 일반 함수의 this로 동작한다.

function Circle(radius) {
  this.radius = radius;
}

Circle.prototype.getDiameter = function() {
	return 2 * this.radius;
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);
// new 연산자를 안쓰면 일반 함수로 동작한다. 일반 함수의 this는 전역 객체를 가리킨다.
const circle3 = Circle(100);

console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
// 일반 함수 Circle은 return값이 없으므로 암묵적으로 undefined를 반환한다.
console.log(circle3.getDiameter()); // undefined
console.log(radius); // 100

function.prototype.apply/call/bind 메서드에 의한 간접 호출

function.prototype.apply/call/bind 메서드의 첫번째 인수로 전달한 객체에 바인딩된다.

apply, call, bind : function.prototype의 메서드

apply, call : 함수를 호출하는 기능
arguments 객체와 같은 유사 배열 객체에 배열 메서드를 사용하는 경우에 사용한다.

function convertArgsToArray() {
  const arr = Array.prototype.slice.call(arguments);
  return arr;
}

convertArgsToArray(1, 2, 3); // [1, 2, 3]

bind : 첫번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.

bind 메서드

중첩함수 또는 콜백 함수를 일반 함수로 호출했을 때 전역 객체가 바인딩되는 것을 앞에서 알아보았다.
하지만 그렇게 되면 메서드 내의 중첩함수 또는 콜백 함수의 this가 메서드를 가리켜야 되는데 전역 객체를 바인딩하게 되므로 문제가 생긴다.

이때, bind()메서드의 this메서드 내부의 중첩 함수 또는 콜백 함수의 this 가 불일치할 때 유용하다.

const obj = {
  value: 100,
  foo() {
    setTimeout(function() {
      console.log(this); // window
    }, 100);
  }
};


const obj = {
  value: 100,
  foo() {
    setTimeout(function() {
      console.log(this); // obj
    }.bind(this), 100);
  }
};

화살표 함수

일반 함수를 정의할 때 function 키워드를 많이 사용한다. 하지만 ES6문법이 도입되면서 화살표 함수로 함수를 선언할 수 있다.

// 일반 함수 선언
function foo() {
  console.log(this);
}

// 화살표 함수 선언
const foo = () => {
  console.log(this);
}

화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다. 따라서 화살표 함수 내부에서 this를 참조하면 가장 가까운 상위 스코프 함수의 this를 참조하게 된다.

const obj = {
  value: 100,
  foo() {
    setTimeout(() => console.log(this), 100);
  }
};
profile
단단한 프론트엔드 개발자가 되고 싶은

0개의 댓글