1. 클로저의 개념
- 이미 생명 주기(실행 컨텍스트)가 끝낸 외부 함수의 변수를 참조하는 함수를 클로저라고 한다.
function outerFunc(){
var x = 10;
var innerFunc = function(){console.log(x);}
return innerFunc;
}
var inner = outerFunc();
inner(); //10
· innerFunc가 클로저이다.
· x : 자유변수(free variable)
· 클로저란 자유변수와 엮여있다 란 의미
이해 확인
function outerFunc(arg1, arg2){
var local = 8;
function innerFunc(innerArg){
console.log((arg1+arg2)/(innerArg + local));
}
return innerFunc;
}
var exam1 = outerFunc(2, 4);
exam1(2); //출력 결과 : 6/10
· outerFunc의 변수가 innerFunc의 [[scope]]로 참조되므로 가비지 컬렉션의 대상이 되지 않고, 여전히 접근 가능
· 클로저를 많이 사용하면 callstack이 쌓이므로 메모리 부족 및 성능저하의 원인이 될 수 있다. 적절한 사용 필요.
2. 클로저의 활용
1) 특정 함수에 사용자가 정의한 객체의 메서드 연결하기
function HelloFunc(func){
this.greeting = "hello";
}
HelloFunc.prototype.call = function(func){
func ? func(this.greeting) : this.func(this.greeting);
}
var userFunc = function(greeting){
console.log(greeting);
}
var objHello = new HelloFunc();
objHello.func = userFunc;
objHello.call(); // 출력 결과 : hello
다인자 함수
function saySomething(obj, methodName, name){
return (function(greeting){
return obj[methodName](greeting, name);
});
}
function newObj(obj, name){
obj.func = saySomething(this, "who", name);
return obj;
}
newObj.prototype.who = function(greeting, name){
console.log(greeting + " " + (name || "everyone"));
}
var obj1 = new newObj(objHello, "zzoon");
obj1.call(); // 출력 결과 : hello zzoon
2) 함수의 캡슐화
"I am XXX, I live in XXX, I'am XX years old" 라는 문장을 출력하는데 XX 부분은 사용자로부터 인자로 입력받아 값을 출력하는 함수
var buffAr = [
'I am ',
'',
'. I live in ',
'',
'. I\'am ',
'',
' years old.'
];
function getCompletedStr(name, city, age){
buffAr[1] = name;
buffAr[3] = city;
buffAr[5] = age;
return buffAr.join('');
}
var str = getCompletedStr('zzoon', 'seoul', 16);
console.log(str);
buffArr은 전역 변수로서 외부에 노출되어 있다. 값이 변경될 수 있고, 같은 이름의 변수로 버그가 생길 수 있다.
클로저를 이용한 개선 방법
var getCompletedStr = (function(){
var buffAr = [
'I am ',
'',
', I live in ',
'',
', I\'am ',
'',
' years old.'
];
return (function(name, city, age){
buffAr[1] = name;
buffAr[3] = city;
buffAr[5] = age;
return buffAr.join('');
});
})();
var str = getCompletedStr('zzoon', 'seoul', 16);
console.log(str);
3) setTimeout()에 지정되는 함수의 사용자 정의
- setTimeout함수는 웹 브라우저에서 제공하는 함수인데 첫 번째 인자로 넘겨지는 함수 실행의 스케쥴링을 할 수있다.
두 번째 인자인 밀리 초 단위 숫자만큼의 시간 간격으로 해당 함수를 호출한다.
자기 자신(this)를 넘겨 주려면 어떻게 해야할까?
function callLater(obj, a, b){
return (function(){
obj["sum"] = a + b;
console.log(obj["sum"]);
});
}
var sumObj = {
sum : 0
}
var func = callLater(sumObj, 1, 2);
setTimeout(func, 500);
3. 클로저를 활용할 때 주의사항
1) 클로저의 프로퍼티 값이 쓰기 가능하므로 그 값이 여러 번 호출로 항상 변할 수 있음에 유의해야 한다.
function outerFunc(argNum){
var num = argNum;
return function(x){
num += x;
console.log('num: ' + num);
}
}
var exam = outerFunc(40);
exam(5);
exam(-10);
//exam의 값을 호출할 때마다 자유변수 num의 값은 계속 변한다.
2) 하나의 클로저가 여러 함수 객체의 스코프 체인에 들어가 있는 경우도 있다.
function func(){
var x = 1;
return {
func1 : function(){ console.log(++x);},
func2 : function(){ console.log(--x);}
};
};
var exam = func();
exam.func1();
exam.func2();
//각각의 함수가 호출될때마다 x의 값이 변화한다.
3)루프 안에서 클로저가 활용될 때는 주의하자
function countSeconds(howMany){
for(var i = 1; i <= howMany; i++){
setTimeout(function(){
console.log(i);
}, i * 1000);
}
};
countSeconds(3);
// 출력 결과 : 4 4 4
올바르게 수정하기
function countSeconds(howMany){
for(var i = 1; i <= howMany; i++){
(function (currentI){
setTimeout(function(){
console.log(currentI);
}, currentI * 1000);
}(i));
}
};
countSeconds(3);
'Language > Java Script' 카테고리의 다른 글
객체지향 프로그래밍 응용 예제 (0) | 2020.03.05 |
---|---|
객체지향 프로그래밍 (0) | 2020.03.04 |
자바스크립트 스코프 체인 (0) | 2020.02.24 |
자바스크립트 실행 컨텍스트 (0) | 2020.02.24 |
자바스크립트 프로토타입 체이닝 (0) | 2020.02.24 |