JavaScript #5 – 함수 지향 (Function-Oriented)
클로저 (Closure)
내부 함수가 외부 함수의 Context에 접근할 수 있는 것을 가리킴.
JavaScript는 함수 안에서 또 다시 함수를 선언할 수 있음. 외부함수 안에 선언된 함수를 내부함수라고 할 때, 외부함수의 지역변수에 내부함수는 접근할 수 있음.
이러한 관계에서 외부함수가 실행이 끝나서 소멸된 이후에도 내부함수가 소멸된 외부함수의 변수에 접근할 수 있는 메커니즘을 ‘클로저’라고 함.
function outter(){
var title = 'coding everybody';
return function(){
alert(title);
}
}
inner = outter();
inner();
위 코드를 살펴보면 inner에는 outter의 결과가 대입되는데, 그 결과가 이름 없는 함수임. inner(); 명령을 실행할 때는 이미 outter 함수의 실행은 끝났기 때문에 outter 함수의 지역변수인 title은 소멸되어야 하지만, 실행 결과는 title의 문자열인 ‘coding everybody’가 그대로 나온다. 이는 title이 소멸되지 않았음을 의미함.
이를 통해 의부함수는 외부함수의 지역변수를 사용하는 내부함수가 소멸되기 전까지 소멸하지 않는 것을 알 수 있음. 이런 특성이 바로 ‘클로저’.
더 복잡한 예시:
function factory_movie(title){
return {
get_title : function (){
return title;
},
set_title : function(_title){
title = _title
}
}
}
ghost = factory_movie('Ghost in the shell');
matrix = factory_movie('Matrix');
alert(ghost.get_title());
alert(matrix.get_title());
ghost.set_title('공각기동대');
alert(ghost.get_title());
alert(matrix.get_title());
Ghost 변수와 matrix 변수는 각각 factory_movie 함수의 리턴값으로 객체를 받고 있음. 둘 다 title이라는 factory_movie의 지역변수를 사용하지만 alert 결과에서 볼 수 있듯이 각각 다른 결과를 보여주고 있음. 이를 통해 외부 함수가 실행될 때마다 새로운 지역변수를 포함시키는 클로저가 생성되어, ghost와 matrix에 있는 title은 완전히 독립된 개체가 되었음을 알 수 있음.
JavaScript는 Private 속성을 지원하지 않음. 하지만 위의 factory_movie 함수의 지역변수 title은 함수 안에서 정의된 메소드에 의해서 만들어진 객체만이 값을 읽고 수정하는게 가능하다는 클로저의 특성을 이용해 Private 속성을 구현할 수 있음.
다음 예시를 보자.
var arr = []
for(var i = 0; i < 5; i++){
arr[i] = function(){
return i;
}
}
for(var index in arr) {
document.write(arr[index]()+'<br/>');
}
0, 1, 2, 3, 4가 출력될 것이라고 예상하기 쉬움. 하지만 for문은 함수가 아님. 그렇기 때문에 for문에 있는 i값에 함수 function이 접근할 수 없기 때문에, for문이 모두 돌고 난 뒤 5가 된 i값이 한번에 적용된 것. 원래 의도대로 출력되게 하려면 다음과 같이 바꿔야함.
var arr = []
for(var i = 0; i < 5; i++){
arr[i] = function(id) {
return function(){
return id;
}
}(i);
}
for(var index in arr) {
document.write(arr[index]()+’<br/>’);
}
Arguments
함수에는 arguments라는 숨겨진 유사 배열이 있음. 함수를 호출할 때 입력한 인자가 담겨 있음.
function sum(){
var i, _sum = 0;
for(i = 0; i < arguments.length; i++){
document.write(i+' : '+arguments[i]+'<br />');
_sum += arguments[i];
}
return _sum;
}
alert('result : ' + sum(1,2,3,4));
Arguments는 함수 안에서 사용할 수 있도록 그 이름이나 특성이 약속되어 있는 arguments 객체의 인스턴스임. 배열과 비슷하지만 배열은 아님. arguments[0]은 함수에 전달된 첫 번째 인자를 알아낼 수 있음.
매개변수와 관련해 두 가지 수가 있음. 하나는 함수.length, 다른 하나는 arguments.length임. 함수.length는 함수에 정의된 인자의 수를, arguments.length는 함수에 전달된 실제 인자의 수를 의미함.
function zero(){
document.write(
'zero.length : ', zero.length+'<br/>',
'arguments : ', arguments.length+'<br/>'
);
}
function one(arg1){
document.write(
'one.length : ', one.length+'<br/>',
'arguments : ', arguments.length+'<br/>'
);
}
function two(arg1, arg2){
document.write(
'two.length : ', two.length+'<br/>',
'arguments : ', arguments.length+'<br/>'
);
}
zero();
one('val1', 'val2');
two('val1');
함수 호출
function func(){
}
func();
JavaScript는 함수를 호출하는 특별한 방법을 제공함. JavaScript에서 함수는 객체임. 위 예시 코드에서 함수 func는 Function이라는 객체의 인스턴스임. 따라서, func는 Function이 가지고 있는 메소드들을 상속하고 있음. 그 중 Function.apply와 Function.call을 활용해봄.
function sum(arg1, arg2){
return arg1+arg2;
}
alert(sum.apply(null, [1,2]))
---
o1 = {val1:1, val2:2, val3:3};
o2 = {v1:10, v2:50, v3:100, v4:25};
function sum(){
var _sum = 0;
for(name in this){
_sum += this[name];
}
return _sum;
}
document.write(sum.apply(o1));
document.write('<br/>');
document.write(sum.apply(o2));
Sum 함수는 내부에서 객체의 속성을 열거할 때 사용하는 for in 문을 이용해서 객체 자신의 값을 열거한 후에 각 속성의 값을 지역변수 _sum에 더한 후 리턴하고 있음.
메소드 apply의 첫 번째 인자는 함수가 실행될 맥락임. 객체의 메소드로 sum을 만들고 호출한 뒤에 만들었던 sum 역할을 하는 메소드를 삭제한다고 생각하면 됨. sum이 객체 소속의 메소드가 된다는 것은 ‘sum에서 this의 값이 전역객체가 아니라 객체가 된다’는 뜻임. 일반적으로 객체지향 언어에서는 하나의 객체에 소속된 함수는 그 객체의 소유물임. 하지만 JavaScript에서는 함수는 독립된 개체로 존재하고, apply나 call 메소드를 통해 다른 객체의 소유물인 것처럼 실행할 수 있음.
만약 apply의 첫 인자로 null을 전달하면 apply가 실행된 함수 인스턴스는 전역객체를 context로 실행하게 됨. 브라우저의 경우는 함수가 실행된 window가 될 것임.
'Programming > Web Programming' 카테고리의 다른 글
JavaScript Basic #7 (0) | 2017.07.22 |
---|---|
JavaScript Basic #6 (0) | 2017.07.21 |
JavaScript Basic #4 (0) | 2017.07.18 |
JavaScript Basic #3 (0) | 2017.07.16 |
JavaScript Basic #2 (0) | 2017.07.15 |