자바스크립트에서의 this
2023-08-04

MDN에서 설명하는 this

대부분의 경우 this의 값은 함수를 호출하는 방법에 따라 결정됩니다. 실행중에는 할당으로 설정할 수 없으며, 함수를 호출할 때마다 다를 수 있다. ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입했고 ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수 를 추가했다. (이는 렉시컬 컨텍스트 안의 this값을 유지한다.)

this는 함수의 실행 컨텍스트(함수가 실행되는 환경)을 가리키는 키워드이다. 자바스크립트에서는 모든 함수는 실행될 때 마다 함수 내부에 this라는 객체가 추가되며 함수가 호출된 방식에 따라서 this의 바인딩이 달라진다.

기본적으로 this는 어디서든지 참조 가능하며 전역에서 this는 전역 객체를 가리킨다.

console.log(this) // window

객체의 메서드를 호출할 때

this는 함수를 실행할 때 함수를 소유하고 있는 객체를 참조한다. 즉 해당 메서드를 호출한 객체로 바인딩 된다.

const obj = {
	a: 10,
	b: 20,
	add() {
		return this.a + this.b
	},
	sub:() => {
		return this.a - this.b
	}
}
const addRes = obj.add()
console.log(addRes) // 30
const subRes = obj.sub()
console.log(subRes) // NaN

여기서 sub 메서드의 실행 결과는 NaN 값이 나온다. sub 메서드는 객체의 메서드로 호출되긴 하였지만 sub 메서드는 화살표 함수로 정의되어 있기 때문이다. 화살표함수의 this 대해서는 아래에서 알아보자.

일반 함수를 호출할 때

특정 객체의 메서드가 아닌 함수를 호출하면, 해당 함수 내부 코드에서 사용된 this는 전역객체에 바인딩 된다.

function add(a, b) {
	return this;
}
const res = add(1,2)
console.log(res) // 브라우저에서는 window Node.js 환경에서는 global

생성자 함수를 통해 객체를 생성할 때

생상자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다. new 키워드를 통해 생성자 함수를 호출할 때는 new 키워드를 통해서 호출된 함수 내부에서의 this는 객체 자신이 된다.

function Add(a, b) {
	this.a = a
	this.b = b
}
const added = new Add(10, 29)
console.log(added.a) // 10
console.log(added.b) // 29

클래스 문법도 마찬가지이다.

class Add {
  constructor(a, b) {
    this.a = a
    this.b = b  
  }
}
const added = new Add(10, 29)
console.log(added.a)
console.log(added.b)

화살표 함수를 호출할 때

위에서 설명한 객체의 메서드를 호출할때 예제를 다시 봐보자

const obj = {
	a: 10,
	b: 20,
	add() {
		return this.a + this.b
	},
	sub:() => {
		return this.a - this.b
	}
}
const addRes = obj.add()
console.log(addRes) // 30
const subRes = obj.sub()
console.log(subRes) // NaN

다른 함수 호출 방법에서 this는 호출될 떄 this의 바인딩이 결정되지만 화살표 함수 에서 this는 선언될 때 결정 된다. 화살표 함수에서 this는 자신을 감싼 정적 범위(lexical context)이며 , 즉 함수가 정의된 위치에서 this를 결정하며 이후에는 변경되지 않는다. 화살표 함수를 bind, call, apply를 통해 호출해도 무시된다.

const obj = {
  message: 'Hello Arrow Function',
};

const arrowFunction = () => {
  console.log(this.message);
};

const bindFunction = arrowFunction.bind(obj);
const calledFunction = arrowFunction.call(obj);
const appliedFunction = arrowFunction.apply(obj);

arrowFunction();   // undefined
bindFunction();   // undefined
calledFunction();  // TypeError: 화살표 함수는 call메서드 사용할 수 없다.
appliedFunction(); // TypeError: 화살표 함수는 apply메서드 사용할 수 없다.

bind, call, apply를 통한 호출

this의 바인딩을 지정할 수 있게 해주는 메서드이다.

  • bind: bind 메서드는 함수를 호출하는 것이 아닌, 새로운 함수를 생성하며 새로운 함수는 원본 함수와 동일한 코드를 가지며 지정한 this와 파라미터들을 가지고 호출될 때 사용된다.
  • call: call 메서드는 함수를 호출하는 것이지만, 첫 번째 인자로 전달한 값이 해당 함수의 this로 사용된다. 그리고 함수의 파라미터들을 순서대로 넘겨준다.
  • apply: apply 메서드는 call과 마찬가지로 함수를 호출하며 두 번째 인자로 배열을 받아서 해당 함수의 파라미터들로 사용된다.
const obj1 = {
	message: 'Hello'
}

const obj2 = {
	message: 'Javascript'
}

const obj3 = {
  message: 'World'
}

function printMessage() {
	console.log(this.message)
}

const bindF = printMessage.bind(obj1)
bindF()
printMessage.call(obj2)
printMessage.apply(obj3)

// 출력: Hello Javascript World