this

holang-i·2021년 8월 5일
0
post-thumbnail
post-custom-banner

Javascript 핵심 개념 알아보기 - JS Flow 를 공부하며 정리합니다.

ES6 이전까지의 자바스크립트의 전반적인 흐름을 공부합니다.

this binding은 실행 콘텍스트가 활성화되는 순간에 한다.

실행 콘텍스트는 해당 함수가 호출될 때 활성화되는데 this는 함수를 호출할 때 결정된다.

아래의 호출하는 방법에 따라 this가 바인딩되는 것들에 대해 공부해 볼 것이다.

  • 전역 공간에서의
  • 함수 호출시
  • 메소드 호출시
  • callback 호출시
  • 생성자 함수 호출시

전역공간에서의 this

전역 공간에서는 전역 콘텍스트를 실행하는 객체가 전역 객체이기 때문에
➡️ 브라우저에서는 window
➡️ nodejs에서는 global이 된다.

브라우저

nodejs


함수 호출시 this

함수 호출시에도 전역 객체를 가리킨다.

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

a();


bug?

아래의 c함수 는 b함수 안에서 this를 찍기 때문에 this가 b함수가 될 것 같지만 window를 가리키고 있다. 이건 일종의 버그라고 한다.
다른 쪽에서는 자바스크립트 자체 특성으로 볼 수 있다는 의견도 있다고 한다.

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

b();

위와 같은 문제를 해결하기 위해 ES6에서는 this 바인딩을 하지 않아도 되는 arrow function이 있다.
arrow function을 사용함으로써 바로 위에 있는 콘텍스트에 있는 this를 그대로 가져다 쓸 수 있게함으로써 문제를 해결했다고 한다.

📣   ES5에서는 함수로서 호출했을 때 this는 언제나 전역 객체를 가리키게 된다.


메소드 호출 시

메소드 호출 주체(메소드 명 앞) -> 메소드 앞에 있는 것이 this가 된다.

var d = {
  e: function () {
    function f() {
      console.log(this);
    }
    f();
  },
};

d.e();

위의 코드를 보면 객체 안에 e라는 메소드가 있고, 메소드로서 호출했을 때 this가 다른것으로 호출되는데 메소드로 실행될 때 내부 함수인 f를 다시 호출했을 때 그냥 f()만 놓고보면 전역객체가 나온다.
📣   호출한 형태만 보면 된다.

아래의 코드를 살펴보면

var a = {
  b: function () {
    console.log(this);
  },
};

a.b();

이번에는 a.b.c()의 결과를 보면

var a = {
  b: {
    c: function () {
      console.log(this);
    },
  },
};

a.b.c();

이렇게 함수로서 호출했을 때는 this는 전역 객체를 가리킨다 했는데 그 말은 즉
함수는 전역객체의 메소드라고 보면 좋다


내부 함수에서의 우회법

내부 함수 안에서 this를 출력했을 때도 전역객체가 나오는 것이..생각했던 거와 다르게 찍히기 때문에 이걸 우회하는 방법에 대해서 알아보았다.

우선 기존에 아래와 같이 this의 값이 찍히는 것을 먼저 확인해 볼 것이다.

var a = 10;
var obj = {
  a: 20,
  b: function () {
    console.log(this.a);

    function c() {
      console.log(this.a);
    }
    c();
  },
};

obj.b();

이게.. var로 a를 만들어서 window객체에 등록이 되버려서 윈도우 객체에 있는 a를 읽어오는것이기 때문에 10찍힌다.
(물론 let으로 하면 undefined가 찍힌다.)


this를 다른 변수에 담아서 내부에서 사용

var a = 10;
var obj = {
  a: 20,
  b: function () {
    var self = this;
    console.log(this.a);

    function c() {
      console.log(self.a);
    }
    c();
  },
};

obj.b();

bind함수가 나오기 전, ES6에서 arrow function이 나오기 전까지는 위의 방법밖에 없었다고 한다.


명시적으로 this를 지정하는 방법 3가지

function a(x, y, z) {
  console.log(this, x, y, z);
}

var b = {
  c: 'hoho',
};

a.call(b, 1, 2, 3);
a.apply(b, [1, 2, 3]);

var c = a.bind(b);
c(1, 2, 3);

var d = a.bind(b, 1, 2);
d(3);

call, apply: 전역 객체가 아닌 특정 어떤 값으로 넣고싶을 때, this를 다르게 넣어서 실행할 때 call, apply를 사용한다.

bind: 실행은 하지않고, 새로운 함수 this를 다르게 향하게 해서 새로운 함수를 리턴받는다.

실행할 this를 명시적으로 지정해줘야되는데
call, apply즉시 호출을 하고,
bind새로운 함수를 생성한다. (currying)


callback 호출시

아래의 코드도 기본적으로는 함수 내부에서와 동일하다.

var callback = function () {
  console.dir(this);
};

var obj = {
  a: 1,
  b: function (cb) {
    cb();
  },
};

obj.b(callback);

environment에서 식별자들을 수집하는데 콜백함수 내부에서의 this는 이 콜백함수 자체에서 콜백함수를 넘겨준 대상, 콜백함수 내부의 this는 원래 전역객체를 보는 것이 맞다.

그런데 아래의 코드를 살펴보면 obj를 내가 가진 this로 할거야라고 지정을 해버렸기 때문에 여기서의 this는 전역객체를 보던 게 obj를 보도록 지정한 것이다.
즉, 콜백함수는 지정한 것에따라 달라진다!

var callback = function () {
  console.dir(this);
};

var obj = {
  a: 1,
  b: function (cb) {
    cb.call(this);
  },
};

obj.b(callback);


setTimeout을 살펴보면 this를 별도로 처리하지 않기때문에 window(전역객체)가 나온다.

var callback = function () {
  console.dir(this);
};

var obj = {
  a: 1,
};

setTimeout(callback, 100);

그런데 이것도 전역객체가 아닌 다른것으로 바꿀 수 있는 방법이 있다.
bind를 사용하면 된다.

var callback = function () {
  console.dir(this);
};

var obj = {
  a: 1,
};

setTimeout(callback.bind(obj), 100);


이벤트 핸들러

아래의 코드를 보면 addEventListener라는 것이 클릭했을 경우 콜백함수를 실행하는 방법이 별도로 정의된 게 없다면 전역객체가 나오는 게 맞을텐데
코드 실행결과를 보면 html Dom Elenet가 나온다.
왜 그럴까??

그것은 addEventListener가 콜백함수를 처리할 때 this를 event가 일발생한 그 대상 target으로 하게끔 내부적으로 정의가 되어있기 때문이다.

document.body.innerHTML += '<div id="a">클릭하세요:)</div>';

document.getElementById('a').addEventListener('click', function () {
  console.dir(this);
});


그럼 위의 코드에서 this를 다른 것으로 하고싶을 때는 어떻게 할 수 있을까?
바로 클릭했을 때 콜백으로 실행되는 함수를 넘길 때 bind를 하면된다.

document.body.innerHTML += '<div id="a">클릭하세요:)</div>';
var obj = { a: 1 };

document.getElementById('a').addEventListener('click', function () {
  console.dir(this);
}.bind(obj));


정리

  • 콜백 함수에서의 this는 기본적으로 함수의 this와 같이 전역 객체를 본다.
  • 제어권을 가진 함수가 callback의 this를 명시하는 경우에는 그에 따르게 된다.
  • 하지만 개발자가 this를 바인딩한 채로 callback을 넘기면 또 그에 따르게 된다.
    -> bind가 제일 우선순위가 높다!

생성자 함수 호출 시

생성자 함수로서 호출한다는 얘기는 new 함수이름() 하는 것.
-> 생성자 함수에 있는 내용을 바탕으로 새로운 인스턴스를 생성하는 것이다.
즉, 인스턴스 자체가 this가 되는 것!

function Person(n, a) {
  this.name = n;
  this.age = a;
}

var person1 = new Person('hoho', 28);
console.log(person1);


총 정리

전역공간
: 전역 객체 - window / global

함수 호출
: window / global

메소드 호출
: 메소드 호출 주체 (메소드 명 , 점 앞!)

callback 호출
: 기본적으로는 함수 내부에서와 동일 / 제어권을 넘겨받은 대상이 callback을 어떻게 처리하느냐에 따라 this가 달라질 수 있다.
그렇게 달라질 수 있음에도 불구하고 개발자의 의지로 bind를 이용해서 this를 지정할 수도 있다.

생성자 함수 호출
: 인스턴스를 가리키게 된다.






post-custom-banner

0개의 댓글