본문 바로가기
Javascript

자바스크립트(javascript) 의 함수

by 램쥐뱅 2016. 8. 3.

1. 자바스크립트 함수의 역할


자바스크립트의 함수를 흔히 객체지향 프로그래밍 언어의 메서드쯤으로 생각하는 경우가 많은데 그것은 큰 오해다.

자바스크립트 함수메서드전혀 다른 개념이다.

자바스크립트 함수는 메서드 역할을 할 수 있지만, 객체지향 프로그래밍의 메서드는 자바스크립트의 함수 역할을 할 수 없다.


자바스크립트 객체지향에서 함수가 이해하기 다소 어려운 이유는 메서드와는 다르게 함수가 몇 가지 역할수 수행할 수 있는 요소 이기 때문이다.


 역할 구분 

 설명 

 역할 1

 호출 가능한 루틴으로서의 함수.

 역할 2

 값으로서의 함수.

 - 인자로 전달 가능.

 - 변수에 할당 가능.

 - 다른 함수의 반환값으로 사용 가능.

 역할 3

 다른 인스턴스를 생성할 수 있는 요소.

 객체 타입으로서의 함수. 


대게 이미 호출 가능한 요소로서의 함수, 즉 함수 역할1 에 대해서는 익숙하다.

좀더 활용을 했었다면 역할2 와 같이 함수를 인자로 전달하거나 다른 변수에 할당하는 코드를 작성해본적도 있을 것이다.

이것은 function으로 함수를 정의하는 것만 으로도 함수 객체가 생성되는 것을 의미한다.

함수 객체는 일반 객체와는 다르게 실행 코드 블록을 갖고 있으면서도 일반 객체처럼 멤버를 가질 수도 있다.


function f(){

//실행 코드 블록

}

f.prop = "p";                 // prop 속성 멤버

f.method = function(){ ...};   // method 메서드 멤버


위와 같은 코드는 함수 객체 f 를 정의하는 코드다.

우리가 흔히 '객체' 라고 하는 것은 Object 객체다.

여기서 말하는 f는 '함수 객체'로서 Object  객체와는 다른 속성 및 메서드를 멤버로 갖는 객체다.

또한 f는 객체로서 코드에서 처럼 사용자가 정의한 prop 속성과 method 메서드를 추가할 수도 있다.


마지막으로 함수는 new 와 함께 사용되어 다른 객체를 생성하는 데 사용될수 있다.

함수가 다른 객체를 생성할 수 있다는 것은 결국 자바스크립트에서는 함수가 일반 객체지향 프로그래밍 언어의 클래스와 유사한 역할을 할 수 있다는 의미다.


이렇게 3가지 역할을 할 수 있는 함수가 어떤 역할로 사용될지는 주변 환경에 달렸다.

new 와 함께 사용되면 객체를 생성하는 역할 3 을 수행하고, new 없이 ()만 사용된다면 역할 1 로 작동한다.

만약 값이 필요한 자리에 함수가 사용된다면 역할 2 로 작동한다.

우리가 함수를 정의하면 앞에서 정의한 역할을 모두 수행할 수 있는 구조가 메모리에 정의된다.






2. 함수 모델링


function add(x, y){

return x + y;

}


위와 같은 add()함수를 정의하고 컴파일(파싱) 단계를 거치고 나면 메모리에는 다음처럼 add 함수가 구성된다.



간단한 함수 하나 정의하는데도 구조 모뎅링이 복잡해지는 이유는, 위에서 말한 함수의 3가지 역할을 모두 수행하기 위해서다.

위 그림에서 "프로토타입 객체"를 제외하고 "변수 스코프", "실행 코드 블록", "공개 변수 영역"은 공식적으로 합의된 용어는 아니다.



변수 스코프


변수 스코프는 함수를 정의할 때 사용된 변수가 정의되는 영역이다.

함수 add가 메모리에 정의되면 기본적으로 호출 가능한 코드블록 (return x+y)이 정의 된다.

add 함수의 지역변수인 x, y는 내부에서만 접근할수 있고 외부에서는 접근할수 없고. 그이외에 변수 스코프에는 prototype 이라는 특별한 변수가 함께 정의된다. 이 변수는 내부 코드는 물론 외부에서도 접근할 수 있다.

prototype은 다른 객체를 가리키는 참조 변수로써 위 그림 오른쪽의 add 함수 전용 프로토타입 객체라는 것이 정의되는데, prototype은 바로 이 객체를 가리킨다.



실행 코드 블록 영역


prototype을 제외하고 변수 영역에 정의된 비공개 변수는 외부에서 접근할 수 없다.

이 영역의 변수에 접근할 수 있는 방법은 실행 코드 블록에 있는 내부 코드를 통해 접근하는 방법뿐이다.


흔히 알고 있는 함수 역할 1 은 간단히 변수 스코프와 실행 코드 블록만으로도 설명이 된다.



공개 변수 영역


자바스크립트 객체의 멤버는 런타임에 언제든지 추가, 제거, 대체될수 있다.

함수도 객체로서 멤버가 동적으로 추가될 수 있는데, 이렇게 동적으로 추가되는 멤버는 함수를 정의할 때 정의되는 변수 스코프와는 다른 영역에 정의된다.



프로토타입 객체


함수를 정의하면 모든 함수에는 프로토타입 객체 라는 것이 함께 정의된다.

객체지향 자바스크립트에서는 이 객체가 매우 중요하다. 프로토 타입 객체에 대해서는 프로토타입 멤버 절에서 따로 정리한다.



영역 접근 방법


변수 스코프와 실행코드 블록은 함수역할 1 즉 호출 가능한 조재로서의 역할을 하는 부분이고,

공개 변수 영역은 함수 역할 2 즉 객체로서의 함수와 연관된 부분이다.

그리고 프로토타입 객체는 다른 객체를 생성할 수 있는 요소와 연관된 영역이다.


각 영역에 접근 하는 방법을 표시하면 다음과 같다.



add() 를 총해 add가 정의하고 있는 실행 코드에 접근할수 있고,

프로토타입 객체는 add.prototype 처럼 함수명과 .prototype을 통해 접근할수 있고,

공개 변수 영역의 변수는 add.Description 처럼 함수명과 도트(.)를 이용해 접근 또는 add["Description"] 처럼 함수명과 대괄호([]) 그리고 변수명을 통해 접근할 수 있다.


함수는 외부에서의 접근 방법에 따라 3개의 영역으로 모델링될 수 있다.







3. 함수 정의


함수를 생성하는 3가지 방법을 정리 해본다.

다음 3가지 방법은 모두 최종적으로 add 라는 함수를 생성한다.



함수 정의


흔히 함수를 정의하는 방법으로 "function 함수명(매개변수정의)" 문을 사용한다.


function add(x, y) {

return x + y;

}


프로그램이 컴파일 되는 단계에서 함수 변수인 add가 정의되고, 함수를 호출하면 런타임에 x, y 가 정의된다.



함수 리터럴 이용


var add = function(x, y) {

return x + y;

}


앞에서 정의한 방법에 비해 이름이 없다는 차이가 있다.

앞에서 정의한 함수 add와 동일한 실행 코드 블록을 갖는 함수를 정의해 add라는 변수에 할당 하고 있다.



Function 생성자 사용


흔하지는 않지만 함수를 정의할 때 Function을 사용할 수도 있다.


var add = new Function("x","y", "return x + y;");


제일 마지막에 오는 매개변수가 실행 코드가 된다.

보통 코드를 작성할 때 Function을 사용해 함수를 정의하는 일은 많지 않지만, 객체지향 프로그래밍에서 function이라는 요소는 중요하다.

더 자세한 사항은 Function절에서 별도로 정리한다.







4. 함수 인자 - arguments, callee


자바스크립트 함수를 호출할 때 정의된 매개변수(parameter)의 개수와 호출하는 인자(argument)의 개수가 다른 경우를 본적이 있을것이다.

어떤 경우는 함수에 정의된 배개변수의 수보다 적은 수의 값으로 함수를 호출하는 경우가 있다.

또 때로는 정의된 매개변수가 없는데도 호출하는 쪽에서 인자를 넘겨주는 경우도 있다.

이것은 C, C++, C#, 자바 같은 강력한 타입의 언어에는 없는 자바스크립트의 독특한 특징이다.


이를 이해하려면 먼저 Arguments라는 타입의 객체를 이해해야 한다.

함수를 호출하면 자바스크립트는 호출하는 인자를 차례로 Arguments라는 타입의 객체에 배열처럼 넣어서 이 객체를 호출되는 함수에 전달한다.




Arguments 객체 자체는 배열이 아니다. 그러나 각 인자에 접근할 수 있게 length 속성 및 연산자 "[]"를 지원한다.


함수 내부에서는 arguments[i]를 통해 Arguments 객체를 통해 넘어오는 인자에 접근할 수 있다.



function sumof(){

var total = 0;

for(var i=0; i<arguments.length; i++){

total+=arguments[i];

}

return total;

}


sumof(2,3,4) // 9

sumof(8,7)   // 15


함수에서 정의한 매개변수의 수보다 많은 수의 인자값이 전달 되면 자바스크립트는 Arguments 객체의 값을 앞에서부터 차례로 받아서 매개변수에 할당하고 남는 인자값은 무시해버린다. 이렇게 남은 인자값도 arguments를 이용하면 접근할 수 있다.

반대로 정해진 매개변수의 수보다 적은 수의 값이 넘어오면 앞에서부터 매개변수의 값이 차례로 채워지고 값을 받지 못한 매개변수는 undefined가 된다. 이것이 기본 규칙이다.



callee 속성은 현재 실행되고 있는 함수(함수 객체)를 나타낸다.

this 와도 유사한 개념이다. this 가 함수 역할 3 에서 새롭게 생성된 객체가 자신을 가리키는데 사용되는 방법이라면,

callee는 함수 역할 2 에서 함수 객체 자신을 가리키는 수단이다.



arguments.callee 속성을 사용하면 익명 함수(unnamed function)가 자신을 참조할 수도 있고 재귀 호출도 쉽게 구현할 수 있다.


function makeFactorialFunc() {

return function(x) {

if( x<=1 ) return 1;

return x * arguments.callee(x-1);

}

}


makeFactorialFunc()(5); // 120


makeFactorialFunc()을 호출해 매개변수 x의 값을 전달받는 함수 객체가 하나 반환된다,

그런다음 인자값 5를 이용해 반환된 익명 함수 객체를 호출하면 익명 함수에 대한 재귀 호출이 수행된 후 최종적인 결과같이 result에 할당된다.


arguemts.callee는 익명 함수에서 자신을 참조해서 재귀 호출을 구현할 때 유용하게 사용할 수 있다.







5. Function


자바스크립트에서는 기본적으로 Function 이라는 함수를 제공한다.

이것은 함수 인스턴스를 생성하는 함수이다.


인스턴스를 생성하는 데 사용하는 함수를 특별히 생성자(constructor)라고 하는데 Object가 Object 인스턴스를 생성하는 생성자라면 Function은 함수 인스턴스를 생성하는 생성자라고 할 수 있다.


var add = new Function("x","y", "return x+y");


function add(x, y){

return x+y;

}


위 두개의 코드는 add라는 함수 인스턴스를 생성하는 동일한 표현이다.


Function 생성자를 호출할 때는 문자열 타입의 인자를 몇 개라도 전달할 수 있다.

마지막 문자열이 해당 함수의 구현 내용이 된다. 마지막을 제외한 그 앞의 인자는 함수를 호출하는데 사용하는 인자다.

함수 본문이 길어진다면 마지막 인자에 문자열로 본문을 작성한다는 것은 쉽지 않은 일이다.

따라서 함수를 정의할 때는 Fucntion 보다는 fucntion을 주로 이용한다.



Function, Object, Array


앞에서 정의된 함수 add가 Function의 인스턴스 이듯이 자바스크립트에서는 Object, Array도 Function의 인스턴스 이다.


Object와 Array도 Function의 인스턴스다.


var obj = new Object();    // Object객체 생성

var obj1 = {};             // Object객체 생성 리터럴


var arr = new Array();     // 배열 객체 생성

var arr1 = [];             // 배열 객체 생성 리터럴 


생성자 Object, Array 등을 흔히 "타입" 또는 "타입 객체"라고도 표현한다.

어떻게 표현하든 Object, Array등은 결국 함수임을 기억할 필요가 있다.



Object와 객체 리터럴이 Object 객체를 만들어 내는 역할을 하는 반면 Function과 function은 함수 객체(function object), 즉 함수를 만들어 내는 역할을 한다.



위 그림은 Object 를 이용하여 동일한 구조의 객체, 즉 구성 멤버가 같은 객체를 여러 개 만들어 낼수 있을을 보여준다.

생성되는 객체의 멤버 구조는 Object가 정의 하고있다.




Object와 비슷하게 Function 함수는 Function 객체를 여러개 만들어낼 수 있다.

Object 함수가 만들어내는 객채와, Function이 만들어내는 객체는 둘다 객체이지만 Function객체는 "함수"라는 것이다.

Function 으로 생성된 함수는 모두 Function에서 정의한 멤버를 공유한다.


Object, Array 생성자도 Function의 인스턴스로서 Function에서 정의한 기본적인 멤버를 사용할 수 있다.

객체지향 언어에서 모든 객체가 Object를 상속하듯이 자바스크립트에서는 모든 함수가 Function을 상속한다는 의미다.



이제 Function, Object 생성자(함수), Object 객체의 관계를 정리하면 다음과 같다.








6. 함수 객체


함수의 역할2 함수 객체는 함수가 변수에 할당될 수 있는 값으로 사용될 수 있고 함수가 다른 함수를 호출하는 인자로 사용될 수도 있으며, 다른 함수의 반환값으로도 사용될 수 있다는 것이다.

즉 function 으로 정의된 함수는 다른 타입의 값처럼 사용할 수 있다는 의미이다.


함수는 다른 타입의 값처럼 다른 함수를 호출하는 인자 반환값으로 사용될 수 있고 다른 변수에 할당될 수도 있다.


function add(x, y){

return x+y;

}


var add = function(x, y){

return x+y;

}


위 코드에서 첫 번째 표현은 함수 정의 코드로써 파싱 단계에서 add 객체가 정의되고,

두 번째 표현은 실행코드로서 런타임에 함수가 정의된다.

정의되는 시점의 차이가 있기는 하지만 두 표현이 런타임에 만들어지는 최종적인 메모리 정의는 동일하다.



함수 이름의 실체는 var 변수다.


함수 객체는 일반 객체처럼 사용되다가도 연산자 "()"와 함께 사용되면 해당 함수 객체에서 정의한 실행 코드 블록이 호출된다.


var r = add(1,2);



함수의 멤버 추가


var obj = new Object();        // Object 객체 생성

obj.Description = "I'm object"; // 속성 추가


위 코드처럼 런타임에 동적으로 속성을 추가할 수 있다.


함수도 객체로서 이런 특징을 그대로 가지고 있다.


var add(x,y) { return x+y; }

add.Description = "I'm function";


이렇게 동적으로 추가되는 변수는 함수 내부의 코드에서 접근할 수 없다.


function add(x,y){

alert(Description); //ReferenceError

return x+y;

}

add.Description = "I'm function";

add(1,2);


앞에서 함수를 모델링할 때 접근 방법에 따라 3가지 영역으로 구분했다. Description은 "공개 변수 영역"에 추가되는 속성으로서 add.Description처럼 함수를 총해서만 접근할수 있다.

함수가 정의될 때 초기에 정의한 내부 변수와는 다른 영역에 저장된다고 이해하면 된다.


function add(x, y){

alert(add.Description); // arguments.callee.Description

return x+y;

}


add.Description = "I'm function";

add(1,2);


자바스크립트의 함수는 일반 객체처럼 런타임에 새로운 멤버를 추가할 수 있다.



변수 할당


함수가 자체적으로 객체라는 특성 때문에 다른 변수에 할당하는 것도 가능하다.


var plus = add;



이제 plus 변수를 통해서도 add에서 정의한 속성과 코드 블록 호출이 가능해진다.


var r = plus(1,2);

var d = plus.Description


이렇게 변수에 함수를 할당하는 것이 가능하다는 것은 함수가 다른 함수를 호풀할 때 해당 함수의 인자 값으로 전달될 수도 있음을 의미한다.



7.익명 함수


함수도 이름이 없을 수 있다.


var add = function(x,y) {

return x+y;

}


앞에서 봤듯이 이렇게 함수 리터럴을 이용해 정의된 함수를 익명 함수(unnamed function)라고 한다.

익명 함수는 이름이 없기 때문에 주로 변수에 할당 되거나 함수 인자의 값 또는 반환값으로 사용된다.


var added = function(x,y) {return x+y}(1,2); //함수를 정의하고 바로 호출한다.


익명 함수를 이런 식으로 호출하는것은 단 한 번밖에 할 수 없다.

이런 예를 확장해서 프로그램이 시작할 때 스스로 호출되는 자체 호출 함수(self-invoking) 구조를 구현할 수도 있다.



8. 중첩 함수


var a = function(arg){

var b = function(in) {

return in * 2;

};

return 'result : ' + b(arg);

};



이 코드를 이름이 있는 함수로 변경해서 다시 정리하면 다음과같다.


function a(arg) {

function b(in) {

return in*2;

};

return 'result : '+b(arg);

};


이처럼 함수 내부에 다른 함수를 정의할 수 있는 구조도 가능한데 이렇게 다른 함수 내부에 정의되는 함수를 중첩 함수(nested function)라고 한다.

같은 내부 함수를 다음처럼 되부에서는 직접 호출할 수 없다.


a(2); // 외부 함수 호출 -> result : 4

b(2); // 중첩 함수 호출 => 오류


특정 함수에서만 사용할 기능이라면 외부에 노출시키지 않고 내부에 정의해서 사용할 수 있다.

기억해야 할 점은 내부 함수에서는 내부에 정의된 변수를 참조할 수 있다는 것이다.

외부에서는 접근할 수 없는 내부의 변수에 내부 함수는 점근할 수 있다는 사실을 이용하면 객체지향의 캡슐화와 정보 은닉 이라는 매우 유익하고도 중요한 개념을 구현할 수 있다. (클로저, 클로저 인스턴스)


외부에서는 내부 변수에 접근할 수 없지만, 내부 함수는 내부 변수에 접근할 수 있다.



9. 콜백 함수


함수 객체를 이용하는 전형적인 사례 가운데 하나는 콜백 구조를 구현하는 데 사용하는 것이다.

라이브러리의 함수를 호출하는 프로그램이 있다고 하자. 그리고 라이브러리 함수에서 실행을 마치고 나면 다시 특정 함수(콜백 함수))를 호출해주길 바란다고 해보다. 이러한 구조를 콜백 구조라고 한다.



함수도 객체라는 사실을 이용하면 이런 구조는 쉽게 만들어진다.

그림에서처럼 라이브러리 함수를 호출할 때 콜백될 함수에 대한 객체 참조를 전달하고 라이브러리 함수의 실행이 끝나면 라이브러리 함수에서는 전달된 함수 객체를 호출하면 된다.

이때 다시 호출되는 메인 프로그램의 함수를 콜백 함수(callback function)라 한다.


// 메인 프로그램

function MainProgram() {

var arg;

LibraryFunction(arg, CallbackFunction);

}


//callback function

function CallbackFunction(result) {

//result 이용

}


//라이브러리 함수

function LibraryFunction(arg, callback) {

var data;

...

callback(data);

}


위 코드를 익명 함수를 이용하면 다음과 같이 구현될 수도 있다.


function MainProgram() {

var arg;

LibraryFunction(arg, function(result){

//result 이용

});

}


function LibraryFunction(arg, callback) {

var data;

...

callback(data);

}


이렇게 라이브러리 함수를 호출할 때 콜백 함수에 대한 참조를 인자로 넣을 곳에 직접 익명 함수를 정의해서 참조를 넘겨주는 방식도 많이 사용된다.


jQuery의 함수를 호출하는 예제를 보면 다음과 같은 형태의 코드를 흔히 볼 수 있다.


$.ajax( {

url : "...",

dataType : "json",

success : function() { ...} ,

error : fucntion() {...}

});


객체 리터럴 {}로 만든 객체를 $.ajax() 함수의 인자로 넘겨주는 코드다.

이때 success, error속성은 익명 함수 객체가 인라인으로 정의되어 할당된다.


이벤트 핸들링 구조도 결국은 콜백 구조와 동일하다고 보면 된다.

이벤트에 대한 핸들러를 등록하는 것은 함수 객체를 속성에 할당하는 것과 마찬가지다.

그런 다음 적절한 시점에 이벤트가 발생하면 핸들러를 콜백하는 것이다.













참고.


자바스크립트 객체지향 프로그래밍.























댓글