[JS] 자바스크립트의 Function 호출과 this

Jung Seo Kyung·2019년 4월 30일
6

🧐 자바스크립트에서 this 란 ?

The object that is executing the current function.
즉 현재 함수를 부른 객체가 누구인지를 나타낸다. 함수가 어떻게 호출되냐에 따라서 this가 결정된다.

먼저 함수의 호출 패턴에 대해서 알아보자.

함수는 여러가지의 패턴으로 실행된다.

  • 일반 함수 실행 (Simple Function Invocation)
  • 함수가 object의 member function 일때, 즉 method 일때
  • bind method를 사용하여 호출했을 때
  • 함수가 Constructor function으로 사용될 때

🎡 Function.prototype.call(this, argList)을 이용한 함수 호출

먼저 기본적인 함수 호출이 내부적으로 어떻게 이루어지는 지에 대해서 알아보자. 일반 함수는 Function constuctor에 의해서 만들어지고 함수 호출(invocation)은 Function의 call method에 의해 이루어진다.

Function의 call method은 아래대로 동작한다.

  1. 처음 부터 끝까지 parameter들로 argument list, argList 를 만든다.
  2. 첫번째 parameter는 thisValue 이다.
  3. thisthisValue로 설정하고 argList를 argument list로 설정하면서 함수를 호출한다.
function hello(thing){
 console.log(this + 'says hello' + thing);
}

hello.call('Jake', 'world') // => Jake says hello world

보다 시피 call method의 첫번째 parameter인 Jake를 this로 설정하고, 'world'라는 argument를 하나를 받으면서 함수를 호출했다. 이건 JavaScript 함수 호출 (funcion invocation)의 원시적인 개념이다. 다른 모든 function 호출들도 원시적으로 표현할 수 있다.

🎡 일반 함수 호출 Single Function Invocation

함수를 ()를 사용하여 호출하는 일반적인 경우에서의 this는 global 객체인 window 이다.

function hello(thing){
 console.log(this + 'says hello' + thing);
}
hello('world')
hello.call(window, 'world')

따라서 call method를 이용하여 표현해보면 call의 첫번째 parameter를 window로 설정하여 호출하는 것과 같다.
❗️ strict mode에서는 다르다. strict mode에서 함수 호출한 경우는 this가 undefined이다.

// in strict mode
hello('world')
hello.call(undefined, 'world');

🎡 메소드 Member Functions

함수가 Object의 method로써 호출될 때의 this는 호출한 object를 나타낸다.

var person = {
 name : 'Jake',
 hello : function(thing){
	console.log(this + 'says hello' + thing);
 }
}

person.hello('world');
person.hello.call(person, 'world')
  • person 객체 안의 hello 메소드를 person.hello('world') 로 실행시킬 수 있다.
    person.hello('world')는 call 함수로 표현했을 때 this가 person, argList는 'world'로 하여 나타낼 수 있고 즉 this는 호출한 객체인 걸 알 수 있다.

한번 더 짚고 넘어가자. this는 고정값이 아니라 호출될 때 결정되며 caller가 누군지에 따라 달라진다.

function hello(thing){
 console.log(this + 'says hello' + thing);
}

var person = { name : 'Jake'}
person.hello = hello;

person.hello('world'); // => Jake says hello world
hello('world') // => [object DOMWindow] says hello world

🎡 bind 메소드를 이용한 경우,Function.prototype.bind

function.bind(obj) 메소드는 function과 같은 body, scope를 가지며 첫번째 파라미터로를 this로 고정해버리는 새로운 함수를 만든다. 즉 함수가 어디서 사용되었는지와 상관없이 this가 고정된다.

bind를 쓰는 경우를 아래와 같이 정의해보았다.
1. 객체 안에 정의된 method를 전역 컨텍스트(다른 문맥)에서 사용하고 싶을 때
2. 전역 컨텍스트(다른 문맥)에서 사용하지만 method를 객체가 부른 것 처럼 만들고 싶을 때

var person = {
 name : 'Jake',
 hello : function(thing){
	console.log(this + 'says hello' + thing);
 }
}
var Boundhello = function(thing){ return person.hello.call(person, thing)}

boundhello('world')

하지만 이렇게 boundhello 함수를 만들면, 여전히 boundhello.call('window', 'world')이다. 즉 boundhello에서의 this는 window이다. 어떻게 person으로 고정 시킬 수 있을까?

var bind = function(func, thisValue){
  return function(){
    return func.apply(thisValue, arguments);
 }
}

var boundhello = bind(person.hello, person);
boundhello('world') // 'Jake says hello world'

여기서 arguments는 함수에 전달되는 Array-like object이다. apply method는 call과 거의 동일하게 동작하지만arguments list 대신에 Array-like object를 취급한다. (Function.prototype.call, Function.prototype.apply의 차이)

새로운 bind 함수를 만들어서 method와 객체를 바인딩 해주었다.

이 처럼 bind 메소드를 이용하여서 기존의 함수와 body, scope가 같지만 this가 고정된 새로운 함수를 만들었다.
이런 표현들이 자주 쓰여졌기 때문에 ES5에서는 모든 Function object에 새로운 method bind가 추가되었다.

var boundhello = person.hello.bind(person);
boundhello('world'); 

bind 메소드는 callback 함수로 raw function을 사용해야할 때 매우 유용하게 쓰인다.

var person= {
 name : 'Jake',
 hello: function() { console.log(this.name + " says hello world");
}
  
document.querySelector('div').click(person.hello.bind(person));

🎡 생성자 함수 Constructor function

함수가 new 키워드와 함께 Constructor로써 사용되었을 때의 this는 새로 만들어진 객체이다. 객체와 Constructor에서 살펴보았듯이 생성자 함수를 사용하면 새로운 object가 만들어지고 해당 객체가 this로 리턴된다.

🎡 Arrow functions

arrow functions는 this를 자체적으로 가지고 있지 않다. arrow function 전까지는 this는 고정되어 있지 않고 caller가 누군지에 따라 결정되었다. 하지만 이런 this 문제들은 객체 지향 프로그래밍 스타일과 맞지 않기 때문에
이를 해결하고자 var that = this 같이 this를 고정하는 방식이나, bind function들이 만들어졌다.

Arrow function은 this를 가지고 있지 않으며, enclosing lexical scope에서의 this 를 arrow function 내에서의 this로 정한다.

⛳️참고

3개의 댓글

comment-user-thumbnail
2020년 5월 15일

하지만 이렇게 boundhello 함수를 만들면, 여전히 boundhello.call('window', 'world')이다. 즉 boundhello에서의 this는 window이다. 어떻게 person으로 고정 시킬 수 있을까? 여기 적으신 부분 this 는 object 아닌가요?

1개의 답글
comment-user-thumbnail
2020년 7월 17일

function hello(thing){
console.log(this + 'says hello' + thing);
}

var person = { name : 'Jake'}
person.hello = hello;

person.hello('world'); // => [Object object]says helloworld 아닌가요?

답글 달기