simkkong

[JavaScript] this와 call, apply, bind 본문

Develop/JavaScript

[JavaScript] this와 call, apply, bind

심꽁 2022. 11. 29. 18:26
728x90

this 는 현재 객체를 가리킨다. 정확히는 메서드를 호출할 때 사용된 객체를 의미한다.

즉 아래와 같이 animal 객체 안에서 run 메서드가 실행이 되면 run을 실행한 animal 객체를 가리키며, 전역에서 호출하게 되면, 글로벌한 window 객체를 가리킨다. 

 

let animal = {
  name: '코끼리',
  run() {
    console.log(this.name);
  }
}

animal.run() // '코끼리'  (this == animal)

//or 

class animal {
  constructor() {
    this.name = '코끼리';
  }

  run() {
    console.log(this.name);
  }
}

const elephant = new animal()
elephant.run() // '코끼리'  (this == animal) 


console.log(this); // Window {window: Window, self: Window, document: document, name: '', location: Location, …}

 

쉽게 생각하면, .run() 메서드의 '점 앞'에 있는 animal 이 객체로 지정된 것이라고 생각하면 된다.
console.log 안의 this 는, 지정된 객체가 없기에 전역 객체를 참고하게 되어 window 를 가리키는 것이다.

동일한 메서드를 사용하더라도, 자바스크립트 런타임에 의해 this가 가리키는 곳이 달라진다.

let elephant = { name: '코끼리' };
let rabbit = { name: '토끼' };

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

elephant.run = sayName;
rabbit.run = sayName;

// 'this'는 '점(.) 앞의' 객체를 참조하기 때문에
// this 값이 달라짐
elephant.run(); // '코끼리'  (this == elephant)
rabbit.run(); // '토끼'  (this == rabbit)

 

※ 화살표 함수는 this 를 갖지않기에, 화살표 함수 내에서 this를 사용하면, 외부에서 this를 찾게된다.

더보기
const zoo = {
  name: '동물원',
  animals: ['코끼리', '토끼'],

  animalsRun() {
    this.animals.forEach(
      animal => console.log(`${this.name}에 있는 동물: ${animal}`)
    );
  },
};

zoo.animalsRun();  
// 동물원에 있는 동물: 코끼리
// 동물원에 있는 동물: 토끼

 

화살표 함수는 forEach 에서 사용을 했기에, this가 animalsRun 메서드를 가리켰어야 했으나, 외부 값인 this 를 가리키게 되므로, zoo 를 가리키게 된다.


만약, animal 의 run 메서드를 변수에 할당한 뒤, 실행하게 되면 어떻게 될까?

let animal = {
  name: '코끼리',
  run() {
    console.log(this.name);
  }
}

const elephant = animal.run;
elephant(); // undefined


위와 같이 this는 ainmal 을 가리키지 않고 undefined 가 뜬다. run을 실행시 '점 앞' 에 있는 animal이 아닌, elephant 에 따로 할당되어 실행되었기 때문이다. 결국 this는 window를 가리키고, window.name 이 실행되기에 undefined가 뜨게된다.

 

이렇게 this 가 가리키는 객체가 무엇이냐에 따라, 좀 더 코드를 유연하게 짤 수 있게된다.

call, apply, bind 를 활용하면 this가 가리키는 객체를 변경할 수 있다.

call 의 경우 아래와 같은 문법을 갖는다.

func.call(context, arg1, arg2, ...)
func.call(context, ...args);

 

실행할 함수의 뒤에 .call 을 붙이고, call 의 첫 번째 인수가 this=context 이며 나머지 arg1, arg2.... 들은 실행할 함수 func 들의 인수들이 된다.

 

function sayName(sound) {
  console.log(`${this.name}의 울음소리: ${sound}`);
}

const elephant = { name: '코끼리' };
const wolf = { name: '늑대' };

// call의 첫 번째 인수인 this 영역에 가리키고자 하는 객체를 넣습니다.
sayName.call(elephant, '뿌우우'); // '코끼리의 울음소리: 뿌우우'  (this == elephant)
sayName.call(wolf, '아우우'); // '늑대의 울음소리: 아우우'  (this == wolf)

 

apply의 경우 call 과 유사하다. 아래와 같은 문법을 갖는다.

func.apply(context, args)

apply와 call의 차이점은, 인수를 따로 따로 받을지 아니면 유사 배열 형태(ex arguments, Array) 로 받을지의 차이점이다.

function fruitBasket(args) {
    console.log('바구니에 들은 과일은: ', ...args);
}

let applyWrapper = function() {
    return fruitBasket.apply(this, arguments);
};

let callWrapper = function() {
    return fruitBasket.call(this, ...arguments);
};

applyWrapper(['사과', '배', '체리']);
callWrapper(['사과', '배', '체리']);

 

bind 의 경우, this를 변경을 하지만 함수를 직접 실행하지 않고 반환을 한다. call과 apply의 경우 함수를 직접 실행한다.

 

bind는 말 그대로 함수의 this 를 변경한 후 함수를 반환해준다.

function sayName(sound) {
    console.log(`${this.name}의 울음소리: ${sound}`);
}

const elephant = { name: '코끼리' };

let elephantSay = sayName.bind(elephant, '뿌우우'); 
elephantSay() // '코끼리의 울음소리: 뿌우우'  (this == elephant)

 

참고사이트

https://ko.javascript.info/call-apply-decorators

 

call/apply와 데코레이터, 포워딩

 

ko.javascript.info

https://www.zerocho.com/category/JavaScript/post/57433645a48729787807c3fd

Comments