Front-End

vsoghlv@naver.com

자바스크립트 클로저의 활용

클로저를 활용할 때 주의점은 성능, 자원적인 면에서 약간 손해를 볼 수 도 있다는것이다. 클로저 활용에 있어서는 경험이 가장 중요하다 하니 계속 만들어 보는 수 밖에 없을 것 같다.

특정 함수에 사용자가 정의한 객체 메서드 연결하기

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 을 스코프 체인에서 참조 할 수 있다.