반응형

자바스크립트에서 가장 좋은 점은 함수의 구현 부분이라고 한다. 함수란 "실행 문장의 집합" 이다. 자바스크립트에서 함수는 모듈화, 클로저, 객체생성등 많은 기능을 제공하고 있다.


1. 객체로서의 함수

 함수는 객체이다. 객체이기 때문에 다음과 같이 속성(프로퍼티)를 가질 수 있다.

function userFunc(){

console.log('hello func');

userFuc.name='hello';

 객체는 프로토타입상 부모 객체를 가르키는 속성인 [[prototype]]을 가진다. 크롬 브라우저에서는 __prototype__ 으로 표현된다. 함수 객체는 Function.prototype에 연결된다. Function.prototype 객체는 Object.prototype에 연결된다.

 또한 자바스크립트에서 함수는 일급 객체이기 때문에 변수나 리턴값, 인자등으로 사용할 수 있다. 다음은 인자로 함수를 넘긴 예이다.

> function paramFunc(){

... console.log('param function');

... }

undefined

> function userFunc(inFunc){

... inFunc();

... console.log('user function');

... }

undefined

> userFunc(paramFunc)

param function

user function

undefined

 모든 함수는 [[protype]]과는 다른 prototype이라는 속성을 가지고 있다. 이 속성은 함수를 constructor라는 속성으로 가르키는 객체이다. 다음의 그림은 각 속성들의 관계를 정리한 그림이다.

2. 함수 생성

 자바스크립트에서 함수를 생성하는 방법은 1) 함수 선언문, 2) 함수 표현식, 3) Function() 함수 의 3가지 방식이 있다.

 

2.1 함수 선언문

 함수 객체는 함수 리터럴로 생성 가능하다. 함수 리터럴은 다음과 같이 function이라는 예약어와 옵션사항인 함수 이름, 괄호로 둘러싸인 함수의 매개변수 집합, 중괄호로 둘러싸인 문장들의 집합으로 구성된다.

function userFunc(arg1,arg2,...) {

statement1;

statement2;

....

return statement;

여기에서 함수 이름은 재귀적으로 호출할 때 사용되며, 디버거나 개발 툴에서 함수를 구분할 때도 사용한다. 함수 이름이 주어지지 않은 것을 익명함수라고 한다.매개변수 집합은 0개 이상의 매개변수를 쉼표로 분리하여 열거한다. 함수 리터럴로 생성한 함수 객체는 외부 문맥으로 연결되어 있는데 이를 클로저라고 한다.


2.2 함수 표현식

 함수 표현식은 함수 객체를 변수에 할당하는 것이다. 앞에서 설명한 바와 같이 함수는 일급 객체이기 때문에 하나의 값으로 취급할 수 있다. 하나의 함수를 변수에 할당하여 생성하는 것을 함수 표현식이라고 한다. 다음과 같이 생성한다.

var userFunc=function optionalFuncName(arg1,arg2,.....){

statement1;

statement2;

......

return statement;

}

 optionalFuncName 은 옵션 사항이다. 그리고 함수 표현식에서 optionalFuncName은 이 함수 내부에서 재귀호출할 경우, 또는 디버거 등에서 함수를 구별할 때 사용된다. 하지만 이 함수를 외부에서 접근할 수 없다. 함수 선언문 방식으로 생성한 것과는 선언한 함수를 함수의 내부와 외부에서 접근 가능한 것과는 차이가 있는 부분이다.

> var userFunc=function optFunc(cnt){

... if(cnt<1) return 'done';

... console.log(cnt);

... cnt=cnt-1;

... optFunc(cnt); //userFunc(cnt)로 대체 가능함

... }

> userFunc(3)

3

2

1

> optFunc(5)

ReferenceError: optFunc is not defined .... 

> var userFunc=function(cnt){

... if(cnt<1) return 'done';

... console.log(cnt);

... cnt=cnt-1;

... userFunc(cnt);

... }

undefined

> userFunc(3)

3

2


 상기 표의 왼쪽에서 처럼 함수 이름이 포함된 함수 표현식을 기명 함수 표현식이라고 하며, 기명 함수 표현식에서 함수 이름의 접근에는 주의해야 한다.


2.3 Function() 생성자 함수를 통한 함수 생성

 자바스크립트 함수는 기본적으로 Function() 이라는 기본 내장 생성자 함수로부터 생성된 개체라고 볼 수 있다. 따라서 어떤 방식으로든 함수를 생성하면 이 함수는 Function Object 를 자신의 [[prototype]] 으로 링크를 가져가게 된다. 

 상기에서 보이는 것처럼 함수는 함수 자신을  constructor로 가지는  prototype 객체 링크(상기 1) 와 부모 객체를 가르키는 링크인 [[prototype]] (상기 2)를 가지게 된다. Function생성자를 이용한 함수 생성은 다음과 같다. Function 생성자를 이용하는 방법은 자주 사용되지 않는다.

new Function (arg1,arg2,...,arg N, function Body) 



3. 호출

 함수 호출시 매개변수와 this, arguments라는 추가적인 변수를 더 받게 된다. this는 1) 메소드 호출 패턴, 2) 함수호출 패턴, 3) 생성자 호출 패턴, 4) apply 호출 패턴에 따라서 다르게 바인딩 된다.

 함수 호출은 표현식 뒤에 이어지는 한쌍의 괄호로 호출한다. 


3.1 메소드 호출 패턴

 함수를 특정 객체의 속성으로 저장하는 경우 이런 함수를 메소드라고 한다. 메소드를 호출할 때 this는 메소드를 포함하고 있는 객체에 바인딩 된다. (this는 객체 자체가 됨) 이 호출 방법은 .나 []을 통해서 호출한다. 

 메소드 호출시 호출되는 함수는 자신을 호출한 객체의 값을 this 키워드로 접근할 수 있다.  여기에서 this  바이딩은 호출시에 발생한다. 


3.2 함수호출 패턴

 함수가 객체의 속성이 아닌 경우 함수로서 호출된다.(즉 어떤 객체의 . 이나 [] 로 호출하지 않고 함수 이름으로 직접 호호출 할 경우) 이때 this 는 전역 객체에 바인딩 된다. 이건 this바인딩에 착각을 불러 일으키는데, 더글라스 크록포드는 이를 언어 설계상의 오류라고 한다. 왜냐하면 내부 함수에서 객체의 값을 접근하기 위한 의도로  this를 사용할 경우 이 this는 의도와는 다르게 전역객체의 this에 바인딩 되기 때문이다. 내부 함수를 함수 호출 패턴으로 호출하게 되면 내부 함수의 this 는 전역객체에 바인딩 되는 것이다.

 다음의 예제를 보면 함수에서 착각하기 쉬운 this 바인딩의 사례를 알 수 있다. getName에서 내부에 정의된 함수 innerFunc는 함수 호출 패턴이 적용되어 innerFunc의 this는 global 객체의 this를 가르키게 된다.

 또한 자바스크립트에서 함수 내에 정의된 변수의 스코프도 잘 알아야 한다. getName 에서 정의된  innername은 getName 함수에서만 접근 가능하다. 자바스크립트는 블록단위의 유효범위를 갖는게 아니고 함수 단위의 유효범위를 갖는다. 그리고 특이한 점은 내부 함수는 자신을 둘러싸고 있는 외부 함수의 변수에 접근 가능하다는 것이다. (아래 코드의 4)번 참고)

[의도와 다른 this 바인딩]


> var testMistake={

... name:'object_name',

... getName:function(){

..... var innername='function_name';

..... function innerFunc(){

....... console.log(this.name); //3)

....... console.log(this.innername); //4)

....... }

..... console.log(this.name); //1)

..... console.log(this.innername); //2)

..... innerFunc();

..... }

... }


> testMistake.getName()

object_name //1)

undefined //2)

undefined //3)

undefined //4)

> var name='global_name';

> var innername='global_innername';

> testMistake.getName()

object_name //1)

undefined //2)

global_name //3)

global_innername //4)

[의도에 맞게 변경]


> var testMistake2={

... name:'object_name',

... getName:function(){

..... var that=this;

..... var innername='function_name';

..... function innerFunc(){

....... console.log(that.name); //3)

....... console.log(innername); //4)

....... }

..... console.log(this.name); //1)

..... console.log(innername); //2)

..... innerFunc();

..... }

... }


> testMistake2.getName()

object_name //1)

function_name //2)

object_name //3)

function_name //4)








3.3 생성자 호출 패턴

 생성자 호출 패턴은 new  키워드를 사용해서 호출하는 것이다. 자바스크립트는 프로토타입에 의해서 상속이 이루어지는 언어이다. 하지만 new 키워드는 클래스 기반 언어를 사용하는 듯한 구문이다. 더글라스 크로포드는 "자바스크립트 자체도 자신의 프로토타입적 본성에 확신이 없었던지, 클래스 기반의 언어들을 생각나게 하는 객체 생성 문법을 제공합니다."라고 표현하였다.

 new를 통해서 함수를 호출하면 호출한 함수는 새로운 객체를 생성하고 호출한 함수의 prototype 속성 값에 연결시킨다. 그리고 이 객체를 this 에 바인딩 한다

 new키워드는 호출되는 함수의 return값을 변형 시킨다. return 값이 기본타입일 경우에는 무시하고 함수에서 새롭게 생성된 객체를 반환한다. return 값이 객체인 경우 해당 객체를 리턴한다. 명시적 return 값이 없는 경우 새롭게 생성된 객체를 반환한다.

 생성자 호출 패턴을 위한 함수는 대문자로 시작하는 것이 관례로 되어 있다. 주의해야 하는 것은 생성자 호출 패턴을 위해서 만들어진 함수를 new 를 사용하지 않고 호출하는 경우 이 함수에서 정의된 this는 전역 객체를 가르켜서 전역 변수의 오염을 일으킬 수 있다는 것이다.


3.4 apply/call 호출 패턴

 함수는 객체이기 때문에 자체의 메소드를 가질 수 있다. 함수가 가진 기본 메소드중 apply/call 메소드는 실행상에 this를 선택해서 호출할 수 있다. apply는 배열로 인자를 받고, call은 , 로 구별해서 인자를 받는다 동작은 비슷하다. 어떤 함수 객체의 메소드에 다른 객체를 바인딩하여 호출하고자 할때 사용된다. 예를 들어 다음과 같은 length를 가지는 pseudo-Array에 배열 메소드를 적용하는 것을 들 수 있다.

> var pseudoArray={

... '0':1,

... '1':2,

... '2':3,

... length:3

... }

undefined

> Array.prototype.splice.apply(pseudoArray,[1,1,5])

[ 2 ]

> pseudoArray

{ '0': 1, '1': 5, '2': 3, length: 3 }

> Array.prototype.splice.apply(pseudoArray,[1,1])

[ 5 ]

> pseudoArray

{ '0': 1, '1': 3, length: 2 }


4. 인수 전달 및 반환

4.1 인수 전달

 함수는 전달된 인수들을 저장된 객체를 가르키는 arguments 와 기본 인수 개수를 가르키는 length, 자신을 호출한 객체를 가르키는 caller 등의 속성을 가진다.

 함수 호출할때 매개변수들을 접근할수 있는 객체 argument가 전달된다. argument는 배열처럼 접근 가능하지만 배열이 아니다. 함수에 전달되는 argument 객체는 argument 값을 배열처럼 숫자로 접근할수 있도록 속성을 가지고 있으며, 실제 전달된 argument 개수인 length, 그 argument를 이용하는 함수인 caller 속성을 가진다.



4.2 반환

 함수 호출시 함수의 첫번째 문장부터 실행해서 함수의 }를 만날때까지 실행된다. 중간에 return을 만나게 되면 함수의 제어를 반환한다. 함수는 항상 값을 반환하며 명시적으로 반환값을 지정하지 않으면 undefined가 반환된다. new 를 통해서 호출하는 경우 반환값이 객체가 아닌 경우 반환값은 this 가 된다.


> function testReturn(){

... console.log('return basic type');

... return 123;

... }

undefined

> function testReturn2(){

... console.log('return object type');

... return {name:'hello',greet:'world'};

... }

undefined

> var obj1=new testReturn()

return basic type

undefined

> obj1

testReturn {}

> var obj2=new testReturn2();

return object type

undefined

> obj2

{ name: 'hello', greet: 'world' }

>  



참고자료

 - 더글라스 크락포드의 자바스크립트 핵심 가이드, 한빛미디어, 2008

 - 인사이드 자바스크립트, 한빛 미디어, 2014

반응형
Posted by alias
,