자기 자신을 정의하는 함수(lazy function)
자기 자신을 재정의하는 함수
함수는 동적으로 정의할 수 있고 변수에 할당할 수 있습니다.
새로운 함수를 만들어 이미 다른 함수를 가지고 있는 변수에 할당하면, 새로운 함수가 이전 함수를 덮어 쓰게 됩니다.
어떤 면에서는 이전의 함수 포인터가 새로운 함수를 가리키도록 재사용하는 것이다.
이러한 일을 이전 함수의 본문 내에서 할 수도 있습니다.
이 경우 함수는 자기 자신을 새로운 구현으로 덮어쓰고 재정의할 수 있습니다.
아마도 실제보다 더 복잡하게 들릴 텐데 간단한 아래의 예제를 통해 알아보도록 합니다.
lazy function definition(게으른 함수 선언)
다음의 예제를 살펴보도록 합니다.
var scareMe = function () {
console.log('Boo!!!');
scareMe = function () {
console.log('Boooo~~~~~~~~~');
};
};
// 자기 자신을 정의하는 함수를 사용
scareMe(); // Boo!!! 가 기록
scareMe(); // Boooo~~~~~~~~~ 가 기록
이 패턴은 함수가 어떤 초기화 준비 작업을 단 한번만 수행할 경우에 유용합니다.
불필요한 작업을 반복할 이유가 없기 때문에 함수의 일부는 더 이상 쓸모가 없을 것이고, 이런 경우에 함수가 자기 자신을 재정의하여 구현 내용을 갱신할 수 있습니다.
간단히 말해서 재정의된 함수의 작업량이 적기 이 패턴은 애플리케이션의 성능에 확실히 도움이 됩니다.
이 패턴은 '게으른 함수 선언(lazy function definition)' 이라고도 불리는데 그 이유는 최초 사용 시점 전까지 함수를 완전히 정의하지 않고 있다가 호출된 이후에는 더 게을러져서 더 적게 일하기 때문이다.
이 패턴의 단점은 자기 자신을 재정의한 이후에는 이전에 원본 함수에 추가했던 프로퍼티들을 모두 찾을 수 없게 된다는 점입니다.
또한 함수가 다른 이름으로 사용된다면, 예를 들어 다른 변수에 할당하거나, 객체의 메서드로써 사용되면 재정의된 부분이 아니라 원본 함수의 본문이 실행됩니다.
scareMe()
함수를 다음과 같이 일급 객체로 사용하는 예를 살펴보도록 합니다.
var scareMe = function () {
console.log('Boo!!!');
scareMe = function () {
console.log('Boooo~~~~~~~~~');
};
};
// 1. 새로운 프로퍼티를 추가한다.
scareMe.property = 'properly';
// 자기 자신을 재정의한 함수를 사용한다.
console.log(scareMe.property);
scareMe();
scareMe();
// 2. 다른 이름으로 할당
var prank = scareMe;
// 3. 메서드로 사용하기 위해 정의한다.
var spooky = {
boo: scareMe
};
// 다른 이름으로 할당한 이름으로 호출한다.
prank(); // Boo!!! 가 기록
prank(); // Boo!!! 가 기록
console.log(prank.property); // properly 가 기록
spooky.boo(); // Boo!!! 가 기록
spooky.boo(); // Boo!!! 가 기록
console.log(spooky.boo.property); // properly 가 기록
// 위에서 다른 이름으로 할당, 메서드로 할당한 이후에 자기자신을 재정의한 함수를 사용해 본다.
scareMe(); // Boooo~~~~~~~~~ 가 기록
scareMe(); // Boooo~~~~~~~~~ 가 기록
console.log(scareMe.property); // undefined 가 기록
예제에서 보는 것처럼, 함수가 새로운 변수에 할당되면 예상과 달리 자기 자신을 정의하지 않습니다.
prank()
가 호출될 때마다 콘솔에 "Boo!!" 가 출력됩니다.
또한 전역 scareMe()
함수를 덮어썼는데도 prank()
자신은 여전히 property 프로퍼티를 포함한 이전의 정의를 참조합니다.
spooky 객체의 boo()
메서드로 함수가 사용될 때에도 똑같은 일이 일어납니다.
이 모든 호출들은 계속해서 전역 scareMe()
포인터를 덮어 씁니다.
따라서 마지막에 전역 scareMe()
가 호출되었을 때 비로소 "Boooo~~~~~~~~~" 를 출력하도록 갱신된 본문이 처음르로 제대로 실행됩니다.
또한 scareMe.property 도 더이상 참조할 수 없게 됩니다.