본문 바로가기

Dev.FrontEnd/JavaScript

11. 자바스크립트의 함수 호출과 this

Chapter 11. 함수 호출과 This

모든 함수는 실행할 때마다 this라는 객체가 추가된다.
앞 포스팅에서 설명한 arguments 객체와 함께 함수 내부로 암묵적으로 전달되는 것이다.
자바스크립트에서의 this 는 여러가지 이유로 상황에 맞게 모습을 달리한다.
(그래서 너무 어렵다. console.log()의 힘을 빌려 연습하자)

함수가 호출되는 방식, 즉 호출 패턴에 따른 this 바인딩을 알아보자.

상황 1. 객체의 메서드를 호출할 때 this 바인딩
객체의 프로퍼티가 함수일 경우 메서드라고 부른다.
this는 함수를 실행할 때 함수를 소유하고 있는 객체(메소드를 포함하고 있는 인스턴스)를 참조한다.
해당 메서드를 호출한 객체로 바인딩된다.
code>
1
2
3
4
5
6
7
8
9
10
11
12
13
var myObject = {
name'foo',
sayName: function(){
   console.log(this);
   console.log(this.name);
}
}
 
> myObject.sayName();
 
/* result */
//Object {name: "foo"}
//foo
cs


상황 2. 함수를 호출할 때 this 바인딩
함수를 호출하면, 해당 함수 내부 코드에서 사용된 this 는 전역객체에 바인딩 된다.
이 메소드와의 바인딩 형식의 차이 때문에, 많은 혼란을 가져올 수 있다.
코드를 통해 확인해보자.
code>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var value = 100;
 
var myObject = {
    value: 1,
    func1: function(){
        this.value += 1;
        console.log('func1() called this.value = ' + this.value);
 
        func2 = function(){
            this.value += 1;
            console.log('func2() called this.value = ' + this.value);
 
            func3 = function(){
                this.value += 1;
                console.log('func() called this.value = ' + this.value);
            }
            func3();
        }
        func2();
    }
};
 
myObjec.func1();
cs


이 한계는 this가 참조하고 있는 값을 다른 변수에 저장하여 넘겨주는 방식도 있고,
다른 여러 방법들이 존재한다.
다음 포스팅을 참고하자.


상황 3. 생성자 함수를 통해 객체를 생성할 때 this 바인딩
그냥 함수를 호출하는 것이 아니라 생성자 함수를 호출할 때는 또 this가 다르게 바인딩 된다.

자바스크립트의 생성자 함수는 말 그대로 자바스크립트의 객체를 생성하는 역할을 한다.
생성자 함수라고 해서 다른 형식이 존재하는 것이 아니라, 기존 함수에 new 연산자를 붙여서 호출하면 그 함수는 생성자 함수로 동작한다.
기존의 함수와 차이가 없기 때문에, 생성자 함수 맨 앞은 대문자로 이름을 지어서 사용하는데 있어서 혼란을 막는다.
생성자 함수가 아닌 함수를 호출하게 되면 문제의 this 가 꼬여버리기 때문이다.

생성자 함수를 호출할 때의 this 바인딩은
생성자 함수가 동작하는 방식을 통해 이해할 수 있다.

new연산자를 통해 함수를 생성자로 호출하게 되면,
일단 빈 객체가 생성되고 this가 바인딩 된다.
이 객체는 함수를 통해 생성된 객체이며, 자신의 부모인 프로토타입 객체와 연결되어 있다.
그리고 return 문이 명시되어 있지 않은 경우에는 this로 바인딩 된 새로 생성한 객체가 리턴된다.

code>
1
2
3
4
5
6
7
var Person = function(name){
    console.log(this);
    this.name = name;
};
 
var foo = new Person('foo');
console.log(foo.name); //foo
cs



아까 언급한, 생성자 함수 함수를 new 없이 호출되는 문제에 대해서 다시 살펴보자.
생성자 함수임을 알리기 위해 첫 글자를 대문자로 작성하긴 했지만, 이것으로는 뭔가 부족하다.
이러한 문제를 이미 파악한 선배 개발자분들이 널리 사용하는 패턴이 있다.
일명, 강제 인스턴스 생성하기 패턴!! 코드부터 살펴보자.

1
2
3
4
5
6
7
8
function A(arg){
     if(!(this instance of A))
          return new A(arg);
     this.value = arg || 0;
}
 
var a = new A(100);
var b = A(10);
cs

생성자 함수 A 구문에 if(!(this instance of A)) 가 추가된 것을 볼 수 있다.
'new로 호출된 것이 아니라면~’이라는 뜻이 되겠다!
즉 new로 호출하지 않았다면 내가 new를 붙여줄게! 가 되는 것이다.
var b = A(10); 이 var b = new A(10);이 되는 것이다.
따라서 var b = A(10); 구문에서 b는 전역에 할당되지 않고 생성자 A로부터 새 인스턴스로 생성될 것이다.

++
if(!(this.instanceof arguments.callee))로 쓰면 범용적으로 사용할 수가 있다!



자바스크립트의 골칫덩어리 this의 내부적인 바인딩에 대해 알아보았다.
이렇게 내부적으로만 바인딩 된다면 정말 골칫 덩어리 일 것이다.
그래서 자바스크립트는 this를 특정 객체에 명시적으로 바인딩 시키는 방법도 제공한다.
바로,  apply( )메서드와 call( )메서드이다.
이 메서드들은 모든 함수의 부모 객체인 Function.prototype 객체의 메서드이므로, 모든 함수에서 호출 가능하다.

두 메서드는 넘겨받는 인자만 다를 뿐 하는 역할은 같기 때문에, 두 메서드를 동시에 설명한다.
apply 메서드를 호출하는 주체가 함수고 메서드의 본질적인 역할은 함수 호출이다.
function.apply(thisArg, argArray);

여기에서 thisArg는 메서드를 호출한 function 내부에서 사용할 this에 바인딩할 객체이다.




-The end-