[Javascript] this

2021. 9. 12. 12:58

this란

javascript의 this는 다른언어와 조금 다르게 동작합니다. 일반적으로 this는 함수 내에서 함수 호출 맥락(context)를 의미합니다. 맥락이라는 것을 상황에 따라서 달라진다는 뜻으로 함수를 어떻게 호출하느냐에 따라서 this가 가리키는 대상이 달라지는 것을 의미합니다.

함수와 객체의 관계가 느슨한 자바스크립트에서 this는 이 둘을 연결시켜주는 연결점의 역할을 합니다.

브라우저 javacript 함수 내의 this

브라우저에서 javascript 파일에 바로 선언하고 사용하면 default 객체에 선언이 되고 default.func()식으로 불리게됩니다. 브라우저에서는 default객체가 window이며, node 환경에서는 global입니다.

function func() { 
  console.log(window === this); // 브라우저에서 true 
  console.log(global === this); // node에서 true 
}

func()

엄격 모드

엄격 모드에서는 javascript 파일에 바로 선언한 함수를 부르게 될시 기본적으로 해주던 default객체의 함수를 부르지않고 해당 함수를 바로 부르게 되어 this에 undefined가 설정됩니다.

function f2() {
  "use strict"
  console.log(this);
}

f2() // undefined
window.f2() // window

메소드의 호출 (객체 안의 함수)

메소드안에서 this를 호출하면 메소드를 가지고 있는 객체를 가리킵니다. 이부분은 다른 언어에서도 동일하게 동작하므로 쉽게 이해가 됩니다.

위의 동작과 조금 다르게 동작하는 것처럼 보이지만 위의 상황과 별반 다르지 않습니다. 왜냐하면 브라우저에서 글로벌로 작성하는 함수는 window 객체에 함수를 만드므로 사실 window객체의 메소드이기 때문이죠.

const object = {
  func: function() {
    console.log(this === object); // true
  }
}

객체에 함수 추가

결국엔 this는 함수 호출시의 참조가 가장 중요합니다. 만약 같은 함수가 여러 객체에 할당되어 있다면 해당 함수를 어떠한 객체를 통해서 호출하는지에 따라 this가 달라집니다.

var o = {prop: 37};

function independent() {
  return this.prop;
}

o.f = independent;
o.b = {g: independent, prop: 42};


console.log(o.f()); // logs 37
console.log(o.b.g()); // logs 42
console.log(o.f === o.b.g) // true

생성자의 안에서 호출

함수를 생성자로 사용할때와 그냥 사용할 때가 다르게 동작합니다. global한 함수를 만들게되면 해당 함수는 window 객체의 메소드로 선언되고 실행되므로 this는 window객체를 가르킵니다.

그러나 생성자로 사용될 경우 생성자는 새로운 객체의 함수로 선언되므로 this가 새로운 객체를 가르키게됩니다.

function Func(){
  console.log(this);
};
const object1 = Func(); // window
const object2 = new Func(); // object2

apply, call, bind

apply, call, bind를 사용하면 this의 값을 제어하여 사용할 수 있습니다.

주의할 점은 bind는 한 번만 동작한다는 점입니다. 만약 bind를 해도 원하는 대로 동작하지 않았다면 해당 함수가 상위 객체에서 내려오면서 bind가 이미 사용되었을 수 있습니다.

const bar = {};

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

Func.call(bar);   // bar
Func.apply(bar);  // bar
Func.bind(bar)(); // bar

function f() {
  return this.a;
}

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

var h = g.bind({a: 'yoo'}); // bind는 한 번만 동작함!
console.log(h()); // azerty

Arrow 함수에서의 this

아래처럼 this를 사용하게 되면 Person객체 growUp함수가 불리는 곳이 window 위에서 불리게되므로 this는 window를 가르키게되어 window.age의 값이 매번 1씩 오르게됩니다. 만약에 Person객체를 여러개 만들게되면 window.age 값을 공유해서 사용하는 결과가 나오게 됩니다. 이는 저희가 원하는 동작이 아닙니다.

function Person() {
  // Person() 생성자는 `this`를 자신의 인스턴스로 정의.
  this.age = 0;

  setInterval(function growUp() {
    // 비엄격 모드에서, growUp() 함수는 `this`를
    // 전역 객체로 정의하고, 이는 Person() 생성자에
    // 정의된 `this`와 다름.
    this.age++;
  }, 1000);
}

var p = new Person();

아래처럼 사용하게 되면 Person이 할당될 때 that에 Person객체가 할당됩니다. growUp함수가 호출될때는 마찬가지로 this는 window를 가르키게 되지만 클로저에 있는 that변수에는 여전히 Person객체가 남아있어 that은 Person을 가르키게됩니다.

function Person() {
  var that = this;
  that.age = 0;

  setInterval(function growUp() {
    // 콜백은  `that` 변수를 참조하고 이것은 값이 기대한 객체이다.
    that.age++;
		console.log(that.age);
  }, 1000);
}
var p = new Person();

화살표 함수를 사용하게 되면 보다 직관적으로 사용할 수 있습니다. 화살표 함수는 window.화살표 함수로 불리지 않습니다. 또한 화살표 함수는 자신의 this가 없습니다. 대신 화살표 함수를 둘러싸는 렉시컬 범위(lexical scope)의 this가 사용됩니다. 때문에 현재 범위에서 존재하지 않는 this를 찾을 때, 화살표 함수는 바로 바깥 범위에서 this를 찾는것으로 검색을 끝내게 됩니다.

function Person(){
  this.age = 0;

  setInterval(() => {
    this.age++; // |this|는 Person 객체를 참조
  }, 1000);
}

var p = new Person();

정리

한마디로 정리하면 javascript의 this는 해당 함수가 어떤 객체를 참조해서 불렸는지에 따라 바뀌게 되며, 특정 함수 call, apply, bind와 같은 함수를 사용하여 직접 제어할 수 있습니다. 화살표함수는 자신의 this가 없습니다. 화살표함수를 둘러싼 환경의 this를 사용되므로 화살표 함수를 잘 활용하면 보다 직관적인 코드를 작성할 수 있습니다.

 

참조

https://opentutorials.org/module/532/6571

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

BELATED ARTICLES

more