실행컨텍스트와 this

이짜젠·2021년 10월 12일
1

Javascript에서 매번 헷갈리는 execution context와 this를 정리해본다.

this는 함수가 호출되는 시점에서, 함수의 소유자를 가르킨다.
즉, "this는 실행컨텍스트를 가르킨다".

Context란, 사전적인 의미로는 "문맥, 맥락"이라는 뜻을 가지고 있다.
Execution Context(실행컨텍스트)란 함수가 실행(호출)될때의 맥락을 의미한다고 할 수 있다.
이는 호출하는 함수를 소유하고있는 소유자로도 볼 수있다.

따라서 같은함수라 하더라도 호출되는 시점에서의 소유자가 누구냐에따라 this의 값이 달라지게 된다.

let obj = {
  name: "objContext",
  printThis: function() {
    console.log(this);
  }
};

let obj2 = {
  name: "objContext2",
  printThis: obj.printThis
};

let printThis = obj.printThis;

// 소유자가 obj, this = obj
obj.printThis(); // obj

// 소유자가 obj2, this = obj2
obj2.printThis(); // obj2

// 소유자가 전역환경, this = window
printThis(); // widnow

// 소유자가 button element, this = btnElem
let btnElem = document.createElement("button");
btnElem.addEventListener("click", printThis);
btnElem.click(); // button element 

this 고정시키기

지금까지의 내용을 다시한번 정리하면

this는 실행컨텍스트를 가리키는 값이다.
실행컨텍스트는 실행되는 순간에 정해지기때문에, this의 값은 호출시점에 따라 동적으로 변경된다.

그러나 동적으로 바뀌는 this 값을 고정시킬 수 있는 방법들이 존재한다.

call, apply

Function 클래스의 기본 메소드들이다.
고정시킬 this를 파라미터로 넘겨줌으로써, this 값을 고정시켜 호출할 수 있다.

var obj = {
  name: "objContext",
  printThis: function() {
    console.log(this);
  }
};

obj.printThis() // {name: 'objContext', printThis: ƒ}
obj.printThis.call(this) // Window {0: global, …}
obj.printThis.apply(this) // Window {0: global, …}

둘의 동작은 비슷하나, 파라미터를 넘기는 방식의 차이가 있다.
func.call(thisArg[, arg1[, arg2[, ...]]])
func.apply(thisArg, [argsArray])

bind

마찬가지로 Function 클래스의 기본 메소드다.
call, apply처럼 고정시키고싶은 this를 파라미터로 넘긴다.
차이점은 call, apply는 this를 고정시켜 함수를 호출해주는 반면, bind는 this가 고정된 새로운 함수를 return 해준다.

var obj = {
  name: "objContext",
  printThis: function() {
    console.log(this);
  }
};

var objPrintThis = obj.printThis.bind(obj);
var globalPrintThis = obj.printThis.bind(this);

// 별도의 호출이 필요하다.
objPrintThis() // {name: 'objContext', printThis: ƒ}
globalPrintThis() // Window {0: global, …}

생성자 함수

new 키워드와 함께 호출되는 함수를 생성자함수라고 한다.
일반함수와는 다르게 new 키워드가 특별한 일을 하기때문에 this가 고정되게 된다.

생성자함수가 호출되면 다음의 과정의 일어난다.

  1. new 키워드에의해 새로운 객체공간이 생성
  2. 생성된 객체공간과 this를 바인딩 <- 이 과정에서 this가 고정된다.
  3. 생성된 객체를 반환
function test () { 
  console.log(this) 
}

test() // Window {0: global, window: Window, …}
new test() // {}

Arrow Function

일반적으로 function 키워드를 이용한 함수선언문이나 함수표현식으로 정의된 함수의 this는 실행컨텍스트를 가르킨다.

그러나 es6 부터 지원하는 화살표함수를 이용하면 this의 값을 고정시킬 수 있다.
사실 고정시킨다라기보다 Arrow Function은 this값이 없다.
스코프체인을 따라서 부모스코프의 this에 접근할 뿐이다.
(스코프는 실행컨텍스트가 아닌 렉시컬컨텍스트를 따른다)

렉시컬 컨텍스트
실행시점이 아닌, 함수가 정의되는 시점의 컨텍스트

let obj = {
  name: "objContext",
  printThis: () => {
    // this 값이 없다. 부모스코프(=부모의 렉시컬스코프)인 global을 this로 갖게된다.
    console.log(this); 
  }
};

let obj2 = {
  name: "objContext2",
  printThis: obj.printThis
};

let printThis = obj.printThis;

let btnElem = document.createElement("button");
btnElem.addEventListener("click", printThis);

// 모두 window로 고정이 되었다.
btnElem.click(); // Window {window: Window …}
obj.printThis(); // Window {window: Window …}
obj2.printThis(); // Window {window: Window …}
printThis(); // Window {window: Window …}

참고

https://poiemaweb.com/js-this
https://www.youtube.com/watch?v=PAr92molMHU&list=PLuBMRNcyzsWxcnDdAmJWyWYXuExyP9aS1&index=6

profile
오늘 먹은 음식도 기억이 안납니다. 그래서 모든걸 기록합니다.

0개의 댓글