this

본 내용은 'The Web Developer 부트캠프 2023', 'Mozilla Developer Network'를 참고하여 작성했습니다.


  • JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작한다. 그리고 엄격 모드비엄격 모드에서도 일부 차이가 있다.
  • this란 간단하게 말해 함수를 호출할 때 생성되는 실행 컨텍스트 객체다.
  • 대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정된다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있다.
  • 대표적인 this들을 살표보면
    • 1) 전역 공간의 this : 전역 객체
    • 2) 메소드 호출 시 메소드 내부의 this : 해당 메소드를 호출한 객체
    • 3) 함수 호출 시 함수 내부의 this : 지정되지 않음 !
      • 3)은 JS개발자의 설계상 오류로 이는 화살표 함수 this로 해결한다.
    • 4) 기타등등

📌실행 컨텍스트(Execution Context)란?

  • 실행 컨텍스트는 실행할 코드에 제공할 환경 정보들을 모아놓은 객체 또는 자바스크립트 코드가 실행되고 연산되는 범위를 나타내는 추상적인 개념이다.
    • 코드를 실행한다면 실행 컨텍스트내부에서 실행되고 있는 것이다. 즉, 코드들이 실행되기 위한 환경이자 하나의 컨테이너라 볼 수 있다.
  • 자바스크립트는 동일한 환경에 있는 환경 정보들을 모은 실행 컨텍스트를 콜스택에 쌓아올린 후 실행하여 코드의 환경과 순서를 보장할 수 있게 됩니다.
  • 실행 컨텍스트가 생성되면 실행 컨텍스트는 실행 컨텍스트 스택 안에 하나씩 쌓이게 되고, top에 위치하는 실행 컨텍스트가 현재 실행되고 있는 컨텍스트이다.

📌실행 컨텍스트(Execution Context)의 종류
실행 컨텍스트의 종류는 아래의 세 가지로 나누어진다.

  1. Global Execution Context
    JavaScript EngineGlobal Execution Context 를 생성한 후 JavaScript 코드를 실행하기 시작한다. 코드 실행 시점에 모든 변수들은 이미 메모리 공간에 할당되어 있으며 undefined 로 초기화 되어있는 상태다. global object를 생성하며 this 값에 global object를 참조한다. 전역 실행 컨텍스트는 Call Stack에 가장 먼저 추가되며 프로그램이 종료 될 때 삭제된다.
  2. Functional Execution Context
    함수가 호출될 때마다, 해당 함수에 대한 실행 컨텍스트가 생성된다. 각각의 함수들은 자신만의 실행 컨텍스트를 가지지만 실행 컨텍스트는 함수가 호출이 되어야 만들어진다. 함수 실행이 종료(return)되면 Call Stack에서 제거된다.
  3. Eval Context
    eval 함수 또한 자신 만의 실행 컨텍스트를 가진다. 보안상 취약한 점이 있어 비권장 함수이기 때문에 사용하지 않는다.
var a = 1; // Global Execution Context  
function outer () { // Functional Execution Context  
  function inner () { // Functional Execution Context  
    console.log(a); // undefined
    var a = 3;
    console.log(a); // 3
  }
  inner();
  console.log(a); // 1
}
outer();
console.log(a); // 1

📌엄격 모드란?

  • 자바 스크립트는 기존 기능을 변경하지 않으면서 새로운 기능을 추가해왔기 때문에 호환성 문제가 없었다. 하지만 ES5부터 새로운 기능이 추가되고 기존 기능의 일부가 변경되었다. 기존 기능이 변경되어 호환성에 문제가 생기게 되었고, 이를 방지하기 위한 것이 엄격 모드(strict mode) 이다.
  • 엄격 모드를 사용하면 말 그대로 문법을 엄격하게 검사하여 묵인했던 에러들이 발생한다.
  • 사용법은 ‘use strict’를 스크립트의 시작 혹은 함수의 시작 부분에 선언하면, 엄격 모드로 코드를 작성할 수 있다. 선언하면 취소는 불가능하다.

전역 공간 this

  • 전역 실행 맥락에서 this는 엄격 모드 여부에 관계없이 전역 객체를 참조합니다.
// 웹 브라우저에서는 window 객체가 전역 객체
console.log(this === window); // true

a = 37;
console.log(window.a); // 37

this.b = "MDN";
console.log(window.b);  // "MDN"
console.log(b);         // "MDN"

함수 this

  • 함수 내부에서 this의 값은 함수를 호출한 방법에 의해 좌우된다.

1) 단순 호출

  • 엄격 모드가 아니며 this의 값이 호출에 의해 설정되지 않으므로, 기본값으로 브라우저에서는 window인 전역 객체를 참조한다.
function f1() {
  return this;
}

// 브라우저
f1() === window; // true

// Node.js
f1() === global; // true

2) 엄격 모드

  • 엄격 모드에서 this 값은 실행 문맥에 진입하며 설정되는 값을 유지하므로 다음 예시에서 보여지는 것 처럼 this는 undefined로 남아있다.
function f2(){
  "use strict"; // 엄격 모드 참고
  return this;
}

f2() === undefined; // true

메서드 this

  • 함수를 어떤 객체의 메서드로 호출하면 this의 값은 그 객체를 사용한다.

예제1

var o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37

예제2

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // logs 37

call, apply

  • 간단하게 인자를 this로 만들어주는 기능을 한다.
  • call()은 인수 목록을, 반면에 apply()는 인수 배열 하나를 받는다는 점이 중요한 차이점이다.

예제1

// call 또는 apply의 첫 번째 인자로 객체가 전달될 수 있으며 this가 그 객체에 묶임
var obj = {a: 'Custom'};

// 변수를 선언하고 변수에 프로퍼티로 전역 window를 할당
var a = 'Global';

function whatsThis() {
  return this.a;  // 함수 호출 방식에 따라 값이 달라짐
}

whatsThis();          // this는 'Global'. 함수 내에서 설정되지 않았으므로 global/window 객체로 초기값을 설정한다.
whatsThis.call(obj);  // this는 'Custom'. 함수 내에서 obj로 설정한다.
whatsThis.apply(obj); // this는 'Custom'. 함수 내에서 obj로 설정한다.

예제2

function add(c, d) {
  return this.a + this.b + c + d;
}

var o = {a: 1, b: 3};

// 첫 번째 인자는 'this'로 사용할 객체이고,
// 이어지는 인자들은 함수 호출에서 인수로 전달된다.
add.call(o, 5, 7); // 16

// 첫 번째 인자는 'this'로 사용할 객체이고,
// 두 번째 인자는 함수 호출에서 인수로 사용될 멤버들이 위치한 배열이다.
add.apply(o, [10, 20]); // 34

bind

  • ES5부터 도입한 기술.
  • 함수를 어떻게 호출했는지 상관하지 않고 this 값을 고정할 수 있다.
  • f.bind(someObject)를 호출하면 f와 같은 본문(코드)과 범위를 가졌지만 this는 원본 함수를 가진 새로운 함수를 생성한다. 새 함수의 this는 호출 방식과 상관없이 영구적으로bind()의 첫 번째 매개변수로 고정된다.
function f() {
  return this.a;
}

var g = f.bind({a: 'azerty'});
console.log(g()); // azerty

var h = g.bind({a: 'yoo'}); // bind는 한 번만 동작함! 이미 고정된 azerty가 출력
console.log(h()); // azerty

var o = {a: 37, f: f, g: g, h: h};
console.log(o.a, o.f(), o.g(), o.h()); // 37, 37, azerty, azerty

화살표 함수 this

  • 화살표 함수에서 this는 자신의 값을 갖지않고, 감싼 정적 범위다. 전역 코드에서는 전역 객체를 가리킨다.
  • 화살표 함수는 전역 컨텍스트에서 실행되더라도 this를 새로 정의하지 않고, 바로 바깥 함수나 클래스의 this를 사용한다.
  • 즉, 함수 호출 시 함수 내부의 this는 지정되지 않는 문제를 해결해준다. 덕분에 의도한 대로 **상위 스코프에 this를 바인딩 시킬 수 있다. **
    • 화살표 함수는 this라는 변수 자체가 아예 존재하지 않는다. 그렇기 때문에 상위 환경을 this가 참조하게 된다.
const cat = {
  name: 'Blue Steele',
  color: 'grey',
  breed: 'scottish fold',
  meow: function() {
    const meow2 = function() {
      console.log(this.name);
    }
    meow2();
  }
};

cat.meow();	// undefined
  • meow2 내부의 this는 지정되지 않아서 곧 전역 객체를 가리킴
  • 전역 객체에 name이란 속성은 존재하지 않으므로 undefined가 뜬다.
const cat = {
  name: 'Blue Steele',
  color: 'grey',
  breed: 'scottish fold',
  meow: function() {
    const meow2 = () => {
      console.log(this.name);
    }
    meow2();
  }
};

cat.meow();	// Blue Steele
  • function으로 선언한 함수가 메소드로 호출되냐 함수 자체로 호출되냐에 따라 동적으로 this가 바인딩되는 반면, 화살표 함수는 선언될 시점에서의 상위 스코프가 this로 바인딩된다.

TIL/회고

실행 컨텍스트와 실행 스택(Execution Stack) 개념은 자바스크립트에서 호이스팅, 클로저, 스코프 같은 개념을 이해하는 데 매우 중요한 내용이라고 한다. 이것저것 찾아보니 다른 언어로 알고있던 개념들이 JS는 다른 부분이 많았고, 처음 보는 개념들도 많았다. 나중에 tihs와 함께 다시 공부해야겠다..ㅠ

댓글남기기