this의 이해

최문경·2022년 4월 6일
0

이번 포스트에서는 this란 무엇인지 그리고 this가 어떻게 결정되는지 알아보겠습니다.

추가로 React class components에서 클래스 메서드를 바인딩해야 하는 이유와 방법에 대해 알아보겠습니다.

개요

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-referencing variable)입니다. 그래서 this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조할 수 있습니다.

중요한 건 this 바인딩은 함수 호출 방식에 의해 동적으로 결정된다는 것입니다.
(바인딩이란 식별자와 값을 연결하는 과정을 의미합니다.)


함수 호출 방식과 this 바인딩

1. 일반 함수에서의 호출

let value = 1;

const obj = {
	value: 100,
	foo() {
		console.log("foo's this: ", this); // {value: 100, foo: f}
		
		setTimeout(function(){
			console.log("callback's this: ", this); // window;
			console.log("callback's value: ", this.value); // 1
		}, 100)
	}
}

obj.foo();

위의 코드를 보면 중첩 함수와 콜백 함수를 포함하여 일반 함수로 호출된 모든 함수 내부의 this에는 전역 객체(window)가 바인딩 된다는 것을 알 수 있습니다.

하지만 중첩 함수 또는 콜백 함수는 외부 함수를 돕는 역할을 하므로 외부 함수와 this가 일치하지 않으면 원하는 동작을 구현하기 어려울 수 있습니다.

this 바인딩을 통해 this를 일치시키는 방법은 여러가지가 있습니다.


  • 변수를 만들고 메서드의 this를 저장해서 사용하기
var value = 1;

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

obj.foo();

var value = 1;

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

obj.foo();

  • 화살표 함수 사용하기

위에서 this는 함수 호출 방식에 따라 동적으로 결정된다고 했지만, 화살표 함수의 this는 항상 상위 스코프의 this를 가르킵니다.

var value = 1;

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

obj.foo();

2. 메서드에서의 호출

메서드 내부의 this는 메서드를 호출한 객체에 바인딩됩니다. (주의할 점은 메서드를 소유한 객체에 바인딩되는 것이 아니라는 점입니다.)

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

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

const anotherPerson = {
	name: 'Kim'
};

anotherPerson.getName = person.getName;
console.log(anotherPerson.getName()); // Kim

3. 생성자 함수에서의 호출

생성자 함수 내부의 this에는 생성자 함수가 미래에 생성할 인스턴스가 바인딩됩니다.

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

const circle1 = new Circle(5);
const circle2 = new Circle(10);

console.log(circle1.getDiameter()); // 10;
console.log(circle2.getDiameter()); // 20;

class 메서드에서의 this

React class components 를 통해 class 메서드에서 this는 어떻게 동작하는지 알아보도록 하겠습니다.

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
  }

  handleClick() {
    console.log(this); // undefined
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

자바스크립트에서 클래스 메서드는 기본적으로 바인딩되어 있지 않습니다. 따라서 handleClick 함수가 호출될 때 this는 undefined입니다.

따라서 바인딩을 해주어야 setState를 사용할 수 있는데, 바인딩 해주는 방법은 2가지가 있습니다.


1. bind메서드 사용하기

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    
    // this가 작동하려면 아래와 같이 바인딩 해주어야 합니다.
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log(this); // Toggle
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

constructor에서의 this는 class(Toggle)이기 때문에 위와 같이 constructor에서 this 바인딩을 해주면 handleClick 가 호출될 때 this는 Toggle클래스를 가리킵니다.


2. 화살표 함수 사용하기

클래스 메서드를 화살표 함수로 만드는 문법은 es2022 이전에는 실험적인 문법이었고, Create React App에서 기본적으로 설정되는 문법이었지만 es2022에 포함되었다고 합니다.

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
  }

  handleClick = () => {
    console.log(this); // Toggle
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

레퍼런스

https://ko.reactjs.org/docs/handling-events.html#gatsby-focus-wrapper
https://react.vlpt.us/basic/24-class-component.html
https://poiemaweb.com/es6-arrow-function
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

profile
프론트엔드 공부하고 있습니다!

0개의 댓글