클로저를 활용할 때 주의점은 성능, 자원적인 면에서 약간 손해를 볼 수 도 있다는것이다. 클로저 활용에 있어서는 경험이 가장 중요하다 하니 계속 만들어 보는 수 밖에 없을 것 같다.
특정 함수에 사용자가 정의한 객체 메서드 연결하기
function HelloFunc() {
this.greeting = "hello";
}
HelloFunc.prototype.call = function (func) {
func ? func(this.greeting) : this.func(this.greeting);
};
let userFunc = function (greeting) {
console.log(greeting);
};
let objHello = new HelloFunc();
objHello.func = userFunc;
objHello.call(); //'hello'
위의 예제를 보면 HelloFunc 에는 grreting 이라는 변수가 있고, func 프로퍼티로 참조되는 함수를 call() 로 호출한다. HelloFunc.prototype.call 을 보면 자신의 지역 변수인 greeting 을 인자로 사용해 사용자가 정의한 함수로 넘기는데, 위의 예제를 보면 userFunc() 를 정의하여 objHello.func() 에 참조시켜 greeting 을 console 에 출력시키도록 했다.
그 결과 objHello.call() 은 ‘hello’ 를 출력시키게 된다.
만약 사용자가 원하는 인자를 더 넣어서 HelloFunc 를 이용해 호출을 원한다면 아래와 같이 하면된다.
function HelloFunc() {
this.greeting = "hello";
}
HelloFunc.prototype.call = function (func) {
func ? func(this.greeting) : this.func(this.greeting);
};
let userFunc = function (greeting) {
console.log(greeting);
};
let objHello = new HelloFunc();
objHello.func = userFunc;
objHello.call();
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"));
};
const obj1 = new newObj(objHello, "sun");
위의 const obj1 = new newObj(objHello, "sun");
로 실행되는 것은 결국 첫번째 인자 obj 의 func 프로퍼티에 saySomething 에서 반환되는 함수를 참조하고 반환한다. 즉, obj1 에 넘긴 objHello 객체에서 func 프로퍼티에 참조된 함수만 바뀐 객체가 된다.
따라서 obj1.call() 로 호출이 가능하다.
obj1.call(); //hello sun
위의 예제들은 정해진 형식의 함수를 콜백해주는 라이브러리가 있는 경우, 다른 형식의 사용자 정의함수를 호출하고자 할 때 유용하게 사용할 수 있다. 예를 들어 onClick 등의 프로퍼티에 evt 객체 외에 원하는 인자를 추가해 주고 싶은 경우 활용이 가능하다.
함수의 캡슐화
“I am XX, I live in XXX.” 라는 문장을 출력 하는 함수를 작성 할 때, X 부분을 사용자에게 입력받아 출력한다고 가정해보자. 먼저 생각할 수 있는 방법은 전역 변수로 문장을 저장하고, 전역 변수에 접근하여 받은 인자를 이용해 변수를 수정하는 방법이다.
let textArr = ["I am ", "", ".I live in", ""];
function getCompletedStr(name, city) {
textArr[1] = name;
textArr[3] = city;
return textArr.join("");
}
let str = getCompletedStr("sun", "seoul");
console.log(str); //I am sun.I live inseoul
다만 위 방식은 단점이 하나 있는데 textArr 은 전역 변수로, 외부에서 접근이 쉽게 가능하다는 점이다. 특히 위와 같은 방식은 다른 코드와 통합, 혹은 라이브러리를 만들때 충돌할 가능성이 매우 크다.
이때 클로저를 활용하여 이 문제를 해결할 수 있다.
let getCompletedStr = (function () {
let textArr = ["I am ", "", ".I live in", ""];
return function (name, city) {
textArr[1] = name;
textArr[3] = city;
return textArr.join("");
};
})();
let str = getCompletedStr("sun", "seoul");
console.log(str); //I am sun.I live inseoul
위에서 주의깊게 봐야 할 점은 변수 getCompletedStr 가 익명함수를 즉시 실행 시켜 반환되는 함수를 할당 하는 것이다. 이때 반환된 함수가 클로저가 되고, 이 클로저는 자유변수 textArr 을 스코프 체인에서 참조 할 수 있다.