자바스크립트/자바스크립트 정리

[자바스크립트] 자바스크립트에서의 this

자바스크립트에서의 this

JS

Java 나 C++ 같은 언어에서의 this는 항상 클래스가 생성할 인스턴스를 가리키게 됩니다. 하지만 자바스크립트에서의 this는 함수 호출 방식에 따라서 this 바인딩이 동적으로 일어나게 됩니다.

 

이는 this 바인딩에 대해서 제대로 알지 못하면 예기치 않은 결과를 만들어 낼 수 있다는 것을 이야기합니다. 이번 글에서는 자바스크립트의 this 바인딩이 동적으로 일어나는 경우에 대해서 정리해보도록 하겠습니다.

this 란?

this 는 객체 지향 프로그래밍과 관련이 있습니다. 객체란, 상태를 나타내는 프로퍼티와 동작을 나타내는 메서드를 하나의 논리적인 단위로 묶은 복합 자료구조입니다. 메서드는 객체의 상태인 프로퍼티를 참조, 변경이 가능해야 합니다. 이 경우 메서드의 입장에서는 먼저 자신이 속한 객체를 가리키는 식별자의 참조가 필요합니다.

 

메서드는 생성자 함수에서 정의됩니다. 하지만 인스턴스는 생성자 함수 정의 이후에 생성됩니다. 그렇다면 앞으로 만들어질 인스턴스를 가리키는 식별자는 어떻게 알 수 있을까요? 이를 위해 자바스크립트는 this라는 식별자를 제공합니다. this를 통해 자신이 생성할 인스턴스를 가리킬 수 있습니다.

 

즉, this는 자신이 속한 객체나 생성할 인스턴스를 가리키는 자기 참조 변수입니다. 여기서 중요한 점은 이 this 가 정적으로 정의되는 것이 아닌, 함수 호출 방식에 따라 동적으로 결정된다는 점입니다. 

자바스크립트에서의 this 바인딩이 결정되는 경우

1. 일반 함수 호출

함수를 일반 함수로 호출한다면 함수 내부의 this 는 전역 객체가 바인딩됩니다. 전역에 선언된 전역 함수는 물론이고, 중첩 함수를 포함한 모든 함수들이 일반 함수로 호출된다면 내부의 this는 전역 객체를 가리킵니다.

 

this 는 객체의 프로퍼티, 메서드를 참조하기 위한 자기 참조 변수라고 했습니다. 그렇다면 객체를 생성하지 않는 일반 함수에서의 this는 사실 의미가 없다고 할 수 있습니다. 따라서 보다 엄격한 환경을 제공하는 strict mode 가 적용된 일반 함수 내부에서 this는 undefined 가 바인딩됩니다.

 

2. 메서드 호출

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

 

생성자 함수에 메서드가 정의되었다고 가정해봅시다. 메서드는 사실 프로퍼티에 바인딩되어 있는 함수입니다. 즉, 프로퍼티가 독립적으로 존재하는 별도의 객체(자바스크립트에서 함수는 객체입니다.)를 가리키고 있는 것뿐입니다.

 

따라서 이 메서드는 다른 객체의 프로퍼티에 할당이 가능하고 일반 변수에 할당하면 일반 함수로서의 호출도 가능합니다. (위에서 얘기했듯이 일반 함수의 호출에서 this는 전역 객체를 가리키게 됩니다.)

 

메서드 호출에서의 this 는 호출한 객체에 바인딩됩니다. 이를 통해 생성한 인스턴스에서 메서드 호출 시에 인스턴스 객체에 this 가 바인딩되어 메서드 사용이 가능해집니다.

 

3. 생성자 함수 호출

생성자 함수는 말 그대로 인스턴스(객체) 를 생성하는 함수입니다. 생성자 함수는 new 연산자와 함께 호출하면 생성자 함수로 동작하지만, 그렇지 않으면 일반 함수로 동작하기 때문에 주의해야 합니다. (마찬가지로 일반 함수로의 호출에서 this는 전역 객체!)

 

좀 더 세부적으로 이야기하자면, 생성자 함수가 호출되면 먼저 빈 객체를 생성합니다. 그리고 이 빈 객체에 this 를 바인딩시켜줍니다. 이후 생성자 함수 내부의 프로퍼티나 메서드 등을 this에 등록해준 후 암묵적으로 return 해주어 인스턴스를 생성하게 됩니다. 

4. Function.prototype.apply/call/bind

위 3가지 호출 방식에 따라 this 바인딩은 동적으로 결정되게 됩니다. 하지만 강제로 this 를 결정해서 함수에 전달하는 것 또한 가능합니다. Function.prototype.apply, Function.prototype.call, Function.prototype.bind 의 3가지 경우로 나뉘게 됩니다.

1. Function.prototype.apply, Function.prototype.call

Function.prototype.apply, Function.prototype.call 은 거의 동일하게 동작합니다. 중요한 차이점 하나는 call()의 경우는 , 로 구분되는 인수 리스트를 받지만, apply()의 경우는 인수 배열을 받습니다.

// 인수 배열을 받는다.
Function.prototype.apply(thisArg[, argsArray])

// 인수 리스트를 받는다.
Function.prototype.call (thisArg[, arg1[, arg2[, ...]]])

이 둘의 본질적인 기능은 함수의 호출입니다. 함수를 호출하면서 첫 인수로 전달한 객체를 호출한 함수의 this 에 바인딩합니다.

function getThisBinding() {
  return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// getThisBinding 함수를 호출하면서 인수로 전달한 객체를 getThisBinding 함수의 this에 바인딩한다.
// apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.
console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
// {a: 1}

// call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// {a: 1}

2. Function.prototype.bind

Function.prototype.bind 메서드는 call, apply 와 다르게 함수 호출을 하지 않고 this로 사용할 객체만 전달합니다. 주로 함수 호출이 즉각적으로 필요하지 않은 경우(setTimeout 이 대표적입니다.)에서 사용이 가능합니다. 또한 메서드 내부의 중첩 함수나 콜백 함수의 this 불일치 문제를 해결하기 위해 사용되고는 합니다.

function getThisBinding() {
  return this;
}

// this로 사용할 객체
const thisArg = { a: 1 };

// bind 메서드는 함수에 this로 사용할 객체를 전달한다.
// bind 메서드는 함수를 호출하지는 않는다.
console.log(getThisBinding.bind(thisArg)); // getThisBinding
// bind 메서드는 함수를 호출하지는 않으므로 명시적으로 호출해야 한다.
console.log(getThisBinding.bind(thisArg)()); // {a: 1}

이처럼 자바스크립트는 정적이 아닌 동적 this 바인딩이 일어나게 됩니다. 제대로 알고있지 못한다면 의도치 않은 결과를 일으킬 수 있으므로 주의가 필요합니다. 

 

보다 더 자세한 내용은 아래 링크에서 확인 가능하고, 저 또한 내용들을 공부하며 정리하기 위한 목적으로 글을 남기는 것을 알려드립니다. 읽어주셔서 감사합니다!

 

출처 : https://poiemaweb.com