JavaScript - this

김영한·2021년 9월 20일
0

JavaScript

목록 보기
2/2

참고, 실행 context, arrow function


this

여러 언어들에서 this는 자기 자신이 속한 객체를 뜻한다.
JavaScript에서의 this는 다른 언어들과 같이 new 키워드를 통해서 자신을 뜻하도록 할 수는 있지만, 더 다양한 방법으로 사용된다.

조금 더 자세하게 말하면 JavaScript에서 this는 현재 실행 context이다.(객체와 관련이 깊음)

실행 context란 호출자가 누구냐는 것과 같은 말로 이해하면 된다.(this가 바라보고 있는 어떤 객체 정도로 이해)

this는 함수가 호출되는 방식에 따라 동적으로 결정때문에 this 바인딩을 통해 어떤 값과 연결되는지 확인이 가능하다.

바인딩이란 호출 방식에 따라 this가 특정 객체에 연결되는 것

지금부터 this의 4가지 바인딩 동작 방식에 대해 알아보자

1. 기본 바인딩(전역 객체)

일반 함수 실행 방식으로 말 그대로 this가 전역 객체(window)를 context 객체로 갖는 것이다.

var name = 'red';

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

foo(); // red

전역 스코프에서 정의한 변수들은 전역 객체에 등록된다.

여기서 foo()처럼 함수를 호출하는 방식을 일반 함수 실행 방식이라고 한다.

따라서 var name = redwindow.name = red와 같고 this 객체에 변수를 등록하는 것은 전역 스코프에서 변수를 선언한 것과 같은 의미이다.

다른 예시로


var age = 100;

function foo () {
    var age = 99;
    bar(age)
}
function bar () {
    console.log(this.age);
}

foo();

위 코드에서 bar함수는 foo함수 내부에서 일반 함수 실행 방식으로 실행되고 있으므로 bar함수에 어떤 매개변수를 넘겨주었든 bar함수 내부의 this.agewindow.age를 가리키므로 전역 변수로 선언된 100이 출력된다.

2. 암시적 바인딩(메소드 실행)

암시적 바인딩은 어떤 객체를 통해 함수가 호출되면 그 객체가 this의 context 객체가 된다는 뜻이다.

var b = 100;

function test() {
  console.log(this.a);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.a);
  }
};

obj.func1(); // 20
obj.func2(); // 20

위 예시에서 func1func2obj를 통해 호출되었기 때문에 objthis가 된다.

따라서 출력을 console.log(this.b)로 변경한다면 undefined를 출력하게 된다.

여기서 기본 바인딩과 연관지을 수 있는데

var b = 100;

function test() {
  console.log(this.b);
}

var obj = {
  a: 20,
  func1: test,
  func2: function() {
    console.log(this.b);
  }
};

var obj2 = {
  b: 40
};

obj.func1(); // 20
obj.func2(); // 20

에서

var gFunc1 = obj.func1;
gFunc1(); // 100

obj2.func = obj1.func;
obj2.func(); // 40

위 코드를 추가한다면 gFunc1100이 출력되게 된다.
그 이유는 전역 스코프에서 생성한 변수는 전역 객체에 등록되기 때문에 gFunc1의 this context는 전역 객체가 되어 전역 변수의 b를 출력할 수 있게 되는 것이다.

그와 반대로 obj2.funcobj2 context에서 생성되기 때문에 obj2의 b 변수값을 출력하게 된다.

3. 명시적 바인딩

함수 객체는call, apply, bind 메소드를 가지고 있는데, 이 때 첫 번째 인자로 넘겨주는 것이 this context 객체가 된다.

function test() {
  console.log(this);
}

var obj = { name: "yuddomack" };
test.call(obj); // { name: 'yuddomack' }

위 코드에서 testthis는 window가 아닌 obj가 된다.(이 함수의 this는 이거다라고 명시적으로 알 수 있다!!)

4. 생성자 바인딩

클래스 디자인 패턴 형태로 new 키워드를 통해 바인딩한다.

function foo(a) {
  this.a = a;
  this.qwer = 20;
}

var bar = new foo(2);
console.log(bar.a); // 2
console.log(bar.qwer); // 20

동작 순서를 보면
1. 새 객체가 만들어진다.
2. 새로 생성된 객체의 프로토타입 체인이 호출 함수의 포로토타입과 연결된다.
3. 생성된 객체를 this context로 명시적으로 사용하여 함수가 실행된다.
4. 생성된 객체를 반환한다.

이 말이 무슨 말이냐면

// 1. 새 객체가 만들어짐
var obj = {};
// 2. 새로 생성된 객체의 Prototype 체인이 함수의 프로토타입과 연결됨
Object.setPrototypeOf(obj, foo.prototype); // 프로토타입을 연결합니다. 이 글에서는 무시해도 상관없습니다.
// 3. 1에서 생성된 객체를 context 객체로 사용(명시적으로)하여 함수가 실행됨
foo.call(obj, 2);
// 4. 이 함수가 객체를 반환하지 않는 한 1에서 생성된 객체가 반환됨
var bar2 = obj; // 여기서 foo는 반환(return)이 없으므로 인스턴스가 생성(된 것처럼 동작)

console.log(bar2.a); // 2
console.log(bar2.qwer); // 20

new로 객체를 생성하는 척하면서 실제로는 위 코드와 같은 과정이 일어나는 것이다.(위 과정을 new로 한번에!)

한 마디로 새롭게 만들어진 객체를 가리킨다.

5. Arrow Function(es6)

화살표 함수에서는 this가 특별하게 작동한다.

바로 화살표 함수가 선언된 부분 스코프의 this context를 사용하는 것이다.

function objFunction() {
  console.log(this.foo); // 13 
  return { 
    foo: 25, 
    bar: () => console.log(this.foo) // 13 vs 25
  }; 
} 

objFunction.call({foo: 13}).bar();

bar가 화살표 함수가 아니였다면 메소드를 소유하고 있는 객체를 가리키기 때문에(암시적 바인딩) 25가 출력되어야 하지만
화살표 함수는 Lexical this를 가지기 때문에 상위 환경의 this를 그대로 계승하는 명시적 바인딩과 같이 13을 출력한다.

이런 특징은 콜백 함수 작성시에 유용하게 사용할 수 있는데 콜백도 함수이기 때문에 es5에서는 this가 전역 객체를 가리키는데 반해 es6에서는 상위 환경의 this를 계승 받게 된다.

0개의 댓글