본문 바로가기

JavaScript

함수 호출 방식에 의해 결정되는 this - 1. 함수 호출

자바스크립트의 함수는 호출될 때, 매개변수로 전달되는 인자값 이외에, arguments 객체this를 암묵적으로 전달받는다.

 

자바스크립트의 경우, 함수호출 방식에 의해 this에 바인딩할 객체가 동적으로 결정된다.

다시 말해, 함수를 선언할 때 this에 바인딩할 객체가 정적으로 결정되는 것이 아니고, 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩할 객체가 동적으로 결정된다.

 

 

함수 호출 방식

1. 함수 호출

2. 메소드 호출

3. 생성자 함수 호출

4. apply/call/bind 호출

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

//1. 함수호출
foo(); // window
// window.foo();와 동일

//2. 메소드 호출
var obj = { foo: foo };
obj.foo(); // obj

//3. 생성자 함수 호출
var instance = new foo(); //instance

//4. apply/call/bind 호출
var bar = { name : 'bar' };
foo.call(bar); //bar
foo.apply(bar); //bar
foo.bind(bar)(); //bar

 

함수호출 방식 중, 이번에는 '함수 호출'에 대해 알아보자!

 

 

전역객체(Global Object)는 모든 객체의 유일한 최상위 객체를 의미.

- 일반적으로 Browser-side : window객체를 의미, 

- Server-side(Node.js) : global객체를 의미

// in browser console
this === window // true

// in Terminal
this === global // true

 

전역객체는 전역 스코프(Global Scope)를 갖는 전역 변수(Global variable)를 프로퍼티로 소유.

글로벌 영역에 선언한 함수는 전역 객체의 프로퍼티로 접근할 수 있는 전역 변수의 메소드이다.

var ga = 'Global Variable';

console.log(ga); // 'Global Variable'
console.log(window.ga); // 'Global Variable'

function foo(){ //글로벌영역에 선언한 함수
 console.log('invoked!');
}

window.foo(); //전역 객체(window)의 프로퍼티로 접근할 수 있는 '전역 변수의 메소드'
//'invoked!'


//전역객체인 window객체가 이렇게 구성되어있지 않을까 :)
window {
 ga: 'Global Variable',
 foo: function() {
  console.log('invoked!');
 }
}

 

함수호출 시, 기본적으로 this는 전역객체(Global object)에 바인딩된다. 전역함수는 물론이고, 심지어 내부함수의 경우도 this는 외부함수가 아닌 전역객체에 바인딩된다.

function foo(){ //함수호출 시, this는 전역객체에 바인딩
 console.log("foo's this: ", this); // window
 
 function bar(){ //내부함수의 경우도, this는 전역객체에 바인딩
  console.log("bar's this: ", this); // window
 }
 bar();
}

foo(); //함수 호출

 

 

또한, 메소드의 내부함수일 경우에도 this는 전역객체에 바인딩된다.

var value = 1;

var obj = {
 value: 100,
 foo: function(){ //메소드 호출의 경우, 메소드 내부의 this는 해당 메소드를 소유한 객체에 바인딩.
  console.log("foo's this: ", this); //foo메소드를 소유한 객체인 obj
  console.log("foo's this.value: ", this.value); //obj.value이므로 100
  
  function bar(){ //메소드의 내부함수 호출
    console.log("bar's this: ", this); // window 메소드의 내부함수의 this는 전역객체에 바인딩
    console.log("bar's this.value: ", this.value);  //1 === window.value
  }
  bar();
 }
}'

obj.foo(); //메소드 호출

 

 

콜백함수의 경우에도 this는 전역객체에 바인딩된다.

var value = 1;

var obj = {
 value: 100,
 foo: function(){
  setTimeout(function(){ //콜백함수의 this는 전역객체에 바인딩
   console.log("callback's this: ", this); // window
   console.log("callback's this.value: ", this.value); // 1 === window.value
  }, 100);
 }
};

obj.foo();

 

 

내부함수는 일반함수, 메소드, 콜백함수 어디에서 선언되었든 관계없이 this는 전역 객체를 바인딩한다.

 

내부함수의 this가 전역객체를 참조하는 것을 회피하는 방법은 아래와 같다.

var value = 1;

var obj = {
 value: 100,
 foo: function(){
  var that = this; //메소드 내 this는 해당 메소드를 소유한 객체. 즉, obj
  
  console.log("foo's this: ", this); // obj
  console.log("foo's this.value: ", this.value); //100
  
  
  function bar(){ //내부함수
   console.log("bar's this: ", this); // window (내부함수의 this는 전역객체에 바인딩.)
   console.log("bar's this.value: ", this.value); // 1
   
   console.log("bar's that: ", that); // obj. 이 scope 밖에 있는 that을 가리킨다.
   console.log("bar's that.value: ", that.value); // 100
  },
  bar();
 },
};

obj.foo(); //메소드 호출

 

 

위 방법 이외에도 자바스크립트는 this를 명시적으로 바인딩할 수 있는 apply, call, bind 메소드를 제공한다.

var value = 1;

var obj = {
  value: 100,
  foo: function() { //메소드 호출
    console.log("foo's this: ",  this); // obj 메소드 호출 시 this는 메소드를 소유한 객체에 바인딩
    console.log("foo's this.value: ",  this.value); // 100
    
    
    function bar(a, b) { // 내부함수
      console.log("bar's this: ",  this); //obj
      console.log("bar's this.value: ", this.value); //100
      console.log("bar's arguments: ", arguments);
    }
    bar.apply(obj, [1, 2]);
    bar.call(obj, 1, 2);
    bar.bind(obj)(1, 2);
  }
};

obj.foo(); //메소드 호출

bar 내부의 this값을 obj 객체로 설정하고, bar함수의 매개변수는 배열로 a=1, b=2로 전달되었다.

 

참고사이트:

 

this | PoiemaWeb

자바스크립트의 this keyword는 Java와 같은 익숙한 언어의 개념과 달라 개발자에게 혼란을 준다. Java에서의 this는 인스턴스 자신(self)을 가리키는 참조변수이다. this가 객체 자신에 대한 참조 값을

poiemaweb.com