[JS] this

DongDong·2022년 12월 19일
0

누군가 나에게 자바스크립트에서 가장 헷갈리는 키워드를 고르라고 한다면 나는 this를 이야기할 것 같다.
this 키워드는 모든 함수 스코프 내에 자동으로 설정되는 특수한 식별자로서
숙련된 개발자들 조차도 정확히 무엇을 가르키고 있는지 대답하기가 껄끄러울 것이라고 생각된다.
나 또한 그렇고 그래서 this에 관하여 포스팅하고자 한다.

📌 this란 무엇인가 ?


this란 말이죠오오옹 - ?

this는 기본적으로 실행 컨텍스트가 생성될 때 결정된다.
즉 this는 함수가 실행될 때 결정된다고 말할 수 있는 것이며 함수가 호출되는 시점에 동적으로 정해지는 것이다.

그럼 어떻게해야 this를 나의 의도대로 잘 전달해줄 수 있을까?
this의 바인딩은 크게 4가지로 나눌 수 있다.

📌 this의 4가지 바인딩


기본 바인딩

function foo(){
	console.log( this.value );
}

var value = 10;
foo(); // 10

var 키워드를 통해서 value라는 변수를 전역으로 선언하였다.
이 때 전역 스코프에 변수명과 같은 이름의 전역 프로퍼티가 생성된다.
그리고 foo() 함수 호출시 this.value는 전역 객체 value이며 기본 바인딩이 적용되어
this는 전역 객체( 브라우저 환경에서는 window , Node 환경에서는 global )를 참조한다.
기본 바인딩 규칙이 적용된 것을 알 수 있는 점은 foo() 함수의 호출부를 보면 된다.
foo() 함수는 평범하게 함수 레퍼런스로 호출되었기 때문에 기본 바인딩이 적용되었다.

엄격모드 (strict mode)에서는 전역 객체가 기본 바인딩 대상에서 제외된다고 하며 this는 undefined가 된다고 한다.
따라서 엄격모드에서 전역 객체에 바인딩해주고 싶다면 window.foo() 를 해주도록 하자.


암시적 바인딩

function foo(){
	console.log( this.value )
}

var obj = {
	value : 10,
	foo:foo
}

obj.foo(); // 10

foo() 함수를 obj에서 프로퍼티로 참조하고 있다.
함수가 객체의 프로퍼티 값이면 메소드로서 호출이 된다.
이 때 메소드 내부의 this는 해당 메소드를 소유한 객체 ( foo()를 호출한 obj )에 바인딩된다.
즉 함수 레퍼런스에 대한 콘텍스트 객체가 존재할 때 암시적 바인딩이 되는 것이다.


명시적 바인딩

function foo(){
	console.log(this.name)
}

var obj = {
	name : "dongdong",
	foo:foo
}

var foofoo = obj.foo;
var name = "Kim";

foofoo();

암시적 바인딩에 의해서 dongdong이 출력될 것 같다.
foofoo는 obj의 foo를 참조하는 변수처럼 보이지만 사실은 foo를 직접 가르키는 또 다른 레퍼런스이며
호출부에서 foofoo()를 호출하기 때문에 Kim이 출력이 된다.

위 예제코드는 보기 쉽고 직관성있게 짠 코드라 금방 알아차릴 수 있겠으나
실제로 사용되는 코드에서는 이보다 더욱 this의 행방을 찾기 어려운 경우가 있을 수 있다.

따라서 this를 바인딩하겠다는 의사를 코드에 나타낼 방법이 바로 call() / bind() / apply() 메서드를 이용한 명시적 바인딩이다.

this에 바인딩 할 객체를 첫째 인자로 받아 함수 호출 시 이 객체를 this로 세팅한다.
this를 지정한 객체로 직접 바인딩 하므로 이를 명시적 바인딩이라고 한다.

function foo(){
	console.log(this.value);
}

var obj = {
	value : 2
}

foo.call(obj) // 2

foo.call()에서 명시적으로 바인딩하여 함수를 호출하므로 this는 obj가 되는 것을 보장받을 수 있다.


new 바인딩 ( 생성자 함수 호출 )

// 생성자 함수
function Person(name) {
// 생성자 함수 코드 실행 전 빈 객체를 생성하고 this를 바인딩한다.
  this.name = name;
// this를 통하여 프로퍼티를 생성한다.
// 생성된 객체를 반환한다.
}

var me = new Person('Lee');
console.log(me); // Person {name: "Lee"}

// new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수로 동작하지 않는다.
var you = Person('Kim');

console.log(you); // undefined

기존 함수에 new 연산자를 붙여서 호출하게 되면 해당 함수는 객체를 생성하는 역할을 하는 생성자 함수로 동작하게 된다.

일반적으로 생성자 함수명은 첫 번째 문자를 대문자로 사용하여 혼란을 방지한다.
ex) new Foo();

생성자 함수를 호출하게 되면 this의 바인딩이 다르게 동작하게 되며 아래와 같이 동작한다고 한다.

  1. 빈 객체 생성 및 this 바인딩
    생성자 함수의 코드가 실행되기 전 빈 객체가 생성되고 이 빈 객체가 생성자 함수가 새로 생성하는 객체이다.
    이후 생성자 함수 내에서 사용되는 this는 이 빈 객체를 가리킨다.
    그리고 생성된 빈 객체는 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정한다.
  2. this를 통한 프로퍼티 생성
    생성된 빈 객체에 this를 사용하여 동적으로 프로퍼티나 메소드를 생성할 수 있다.
    this는 새로 생성된 객체를 가리키므로 this를 통해 생성한 프로퍼티와 메소드는 새로 생성된 객체에 추가된다.
  3. 생성된 객체 반환
    반환문이 없는 경우, this에 바인딩된 새로 생성한 객체가 반환된다.
    명시적으로 this를 반환하여도 결과는 같다.
    반환문이 this가 아닌 다른 객체를 명시적으로 반환하는 경우, this가 아닌 해당 객체가 반환된다.
    이때 this를 반환하지 않은 함수는 생성자 함수로서의 역할을 수행하지 못한다.
    따라서 생성자 함수는 반환문을 명시적으로 사용하지 않는다.

일반 함수를 호출하면 this는 전역객체에 바인딩되지만 new 연산자와 함께 생성자 함수를 호출하면 this는 생성자 함수가
암묵적으로 생성한 빈 객체에 바인딩된다.

정리

  • 전역공간에서의 this는 전역객체를 참조한다. ( window , global )
  • 함수를 일반적으로 호출한 경우 this는 전역객체를 참조한다. ( 기본 바인딩 )
  • 함수를 메서드로서 호출한 경우 this는 메서드 호출 주체를 참조한다. ( 암시적 바인딩 )
  • 함수를 호출할 때 call/apply/bind 메서드를 사용하여 this를 명시적으로 지정해줄 수 있다. ( 명시적 바인딩 )
  • 생성자 함수에서의 this는 생성될 인스턴스를 참조한다. ( new 바인딩 )
profile
중요한건 꺾이지 않는 마음

0개의 댓글