'2015/12/07'에 해당되는 글 1건

  1. 2015.12.07 [JavaScript] 자바스크립트 함수에 대하여 2
반응형

<함수호출과 this에 대하여>


함수가 호출될 때 호출 파라미터인 argumentsthis 인자가 전달된다. arguments는 함수를 호출할 때 넘겨진 인자들과 호출할 떄 넘겨진 인자의 개수인 length프로퍼티, 현재 실행중인 함수의 참조값인 callee 프로퍼티로 구성된다. arguments 로 넘겨지는 인자들은 배열형태이기는 하지만 배열은 아니다. 배열의 함수를 이용하려면 apply 또는 call 을 이용해야 한다.


this는 함수가 호출되는 방식에 따라 다른 객체를 참조한다.


1. 객체의 메서드로 호출(메서드 호출 패턴)

객체의 프로퍼티가 함수일 경우 이 함수를 메서드라고 부른다. 메서드를 호출할 때 메서드 함수에서 사용되는 this는 해당 메서드를 호출한 객체로 바인딩 된다. 다음의 예제 코드를 보자

1
2
3
4
5
6
7
8
9
10
11
12
13
var exObject={
    name:'foo',
    greeting:function(){
        console.log('hello '+this.name);
    }
};
 
exObject.greeting();//hello foo
var exObject2={
    name:'bar'
};
exObject2.greeting=exObject.greeting;
exObject2.greeting();//hello bar
cs


이 코드에서 각 객체의 프로퍼티와 this 참조는 다음과 같다.

exObject와 exObject2는 같은 함수를 참조하고 있다. 여기에서 this는 어떤 객체의 메소드로 호출되느냐에 따라서 바인딩이 달라진다.


2. 단독 함수로 호출(함수 호출 패턴)

객체의 프로퍼티가 아닌 함수를 호출하면 this는 전역객체에 바인딩 된다. (브라우저에서는 window 객체, node.js에서는 global 객체) 다음의 코드는 그 예이다.

1
2
3
4
5
function helloGlobal(){
    console.log('Hello '+this.name);
}
var name='bar';
helloGlobal(); //hello var
cs


주의해야 할 것은 어떤 객체의 메서드의 내부 함수의 경우 그냥 단독 함수로 호출한 것으로 간주된다는 것이다. 다시 말해 객체 메서드에서 내부 함수를 정의하고 내부 함수를 호출하면 this가 해당 객체가 아니고 global 객체가 되는 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
var x=1;
var exObject={
    x:100,
    methodFunc:function(){
        console.log(this.x); //여기에서 this는 exObject를 참조, 출력은 100
        var innerFunc=function(){
            console.log(this.x); //여기에서 this는 global을 참조, 출력은 1
        }
        innerFunc(); 
    }
}
exObject.methodFunc();
cs


 위 예제에서 innerFunc는 단독 함수로 호출하는 패턴으로 이 함수에서 this는 global이 된다. 일반적으로 exObject 내부 메서드의 함수이므로 exObject를 가르킬것이라고 생각하기 쉽지만 자바스크립트에서는 메서드 호출이냐 또는 그냥 함수 호출이냐인지가 중요하다. 만약 내부 함수에서 this를 해당 객체로 참조하고 싶으면 함수 정의 전에 다른 변수(예: that) 에 this를 할당하고 내부 함수에서 this 대신 참조하면 된다. 다음은 그 예제이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
var x=1;
var exObject={
    x:100,
    methodFunc:function(){
        console.log(this.x); //여기에서 this는 exObject를 참조, 출력은 100
        var that=this;
        var innerFunc=function(){
            console.log(that.x); //that는 exObject를 참조, 출력은 100
        }
        innerFunc(); 
    }
}
exObject.methodFunc();
cs


3. 생성자 함수로 호출(생성자 호출 패턴)

생성자 함수로의 호출은 기존 함수에 new연산자를 붙여서 호출하면 된다. 일반 함수에 new를 붙이면 원치 않게 생성자 함수로 동작할 수 있으므로 어떤 함수가 생성자 함수라면 해당 함수의 첫문자를 대문자로 쓰기로 권장하고 있다. 


생성자 함수로 호출하게 되면 다음과 같이 동작한다.

1) 빈 객체 생성, 2) 생성한 객체에 this 바인딩, 3) 해당 객체의 [[Prototype]]을 생성자 함수의 prototype 객체로 설정, 4) 생성자 함수 코드 실행, 5) 새로 생성된 함수 리턴


다음의 예제를 확인해 보자. 이 예제는 생성자 함수를 선언하고, 생성자 함수의 prototype 이 가르키는 객체에 greeting 프로퍼티를 추가하였다.

1
2
3
4
5
6
7
8
var ExObject=function(name){
    this.name=name;
};
ExObject.prototype.greeting=function(){
    console.log('Hello '+this.name);
}
var exObject=new ExObject('bar'); //(3)
exObject.greeting(); //Hello bar
cs

다음의 그림은 관련 관계들을 표현한 것이다.

그럼 ExObject를 new 없이 호출하게 되면 어떻게 될까?

1
2
3
4
5
6
7
8
9
10
11
12
var ExObject=function(name){
    this.name=name;
};
ExObject.prototype.greeting=function(){
    console.log('Hello '+this.name);
}
ExObject.prototype.greeting(); //Hello undefined --1)
ExObject('global');
console.log(name); //global
ExObject.prototype.greeting(); //Hello undefined --2)
ExObject.prototype.name='prototype';
ExObject.prototype.greeting(); //Hello prototype --3)

cs

위 예제에서 1)에서는 어느곳에서든 name이 정의되지 않았으므로 undefined이 된다. ExObject를 호출하면 this가 global에 binding 되므로 name을 호출하면 'global'이 출력된다. 2)에서도 출력은 undefined이 나타나는데, 이는 greeting 함수는 ExObject.prototype의 메서드이기 때문에 이 함수에서의 this는 ExObject.prototype이 된다. 따라서 ExObject.prototype.name을 명시적으로 만들어주면 3)에서처럼 출력이 되는 것이다.


4. call 또는 apply 메소드를 이용한 호출

함수의 call 또는 apply 를 호출하게 되면 명시적으로 this를 넘겨줄 수 있다. apply와 call은 동일한데, 넘겨받는 인자의 형식만 다르다. apply는 array로 받으며 call은 매개변수를 직접 명시해주면 된다. 다음의 예제를 보자

1
2
3
4
5
6
7
8
9
var ExObject=function(name){
    this.name=name;
};
ExObject.prototype.greeting=function(){
    console.log('Hello '+this.name);
}
ExObject.prototype.name='prototype';
ExObject.prototype.greeting()//Hello prototype --1)
ExObject.prototype.greeting.apply({name:'apply'}); //Hello apply --2)
cs

1)에서는 ExObject.prototype에 this가 binding되어 'Hello prototype'을 호출하지만 2)에서는 {name:'apply'} 객체를 this로 바인딩하여 'Hello apply'가 출력되는 것이다. 


5. 함수의 리턴

자바스크립트는 항상 리턴값을 반환한다. return문을 사용하지 않았을 경우 일반 함수에서는 undefined가, 생성자 함수에서는 생성된 객체가 리턴된다. 근데 생성자 함수의 리턴값을 객체가 아닌 불린, 숫자, 문자열을 리턴하는 경우 리턴값을 무시하고 this로 바인됭 객체가 리턴된다.


참고자료

http://blog.outsider.ne.kr/439

http://dimdim.tistory.com/entry/javascript-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C-%ED%8C%A8%ED%84%B4

반응형
Posted by alias
,