함수 만드는 방법 세 가지
new Function 문법은 잘 사용되진 않지만, 이 방법 외에는 대안이 없을 때 사용한다.
let func = new Function ([arg1, arg2, ...argN], functionBody);
새로 만들어지는 함수는
인수 arg1...argN과
함수 본문 functionBody
로 구성된다.
인수 두 개가 있는 함수를 직접 만들어 보면서 new Function 문법에 대해 이해해보자.
let sum = new Function('a', 'b', 'return a + b');
alert( sum(1, 2) ); // 3
인수가 없고 함수 본문만 있는 함수를 만들어보겠습니다.
let sayHi = new Function('alert("Hello")');
sayHi(); // Hello
기존에 사용하던 방법과 new Function을 사용해 함수를 만드는 방법의 가장 큰 차이는 런타임에 받은 문자열을 사용해 함수를 만들 수 있다는 점이다.
let str = ... 서버에서 동적으로 전달받은 문자열(코드 형태) ...
let func = new Function(str);
func();
서버에서 코드를 받거나 템플릿을 사용해 함수를 동적으로 컴파일해야 하는 경우, 복잡한 웹 애플리케이션을 구현할 때와 같이 아주 특별한 경우에 new Function을 사용할 수 있다.
함수는 특별한 프로퍼티 [[Environment]]에 저장된 정보를 이용해 자기 자신이 태어난 곳(함수가 만들어진 렉시컬 환경)을 기억(참조)한다.
6.3 유효범위와 클로저 참고
그런데 new Function을 이용해 함수를 만들면 함수의 [[Environment]] 프로퍼티가 현재 렉시컬 환경이 아닌 전역 렉시컬 환경을 참조하게 된다.
따라서 new Function을 이용해 만든 함수는 외부 변수에 접근할 수 없고, 오직 전역 변수에만 접근할 수 있습니다.
function getFunc() {
let value = "test";
let func = new Function('alert(value)');
return func;
}
getFunc()(); // ReferenceError: value is not defined
일반적인 방법을 사용해 함수를 정의한 예시와 비교
function getFunc() {
let value = "test";
let func = function() { alert(value); };
return func;
}
getFunc()(); // getFunc의 렉시컬 환경에 있는 값 "test"가 출력됩니다.
스크립트가 프로덕션 서버에 반영되기 전, 압축기(minifier)*가 지역 변수 이름을 짧게 바꾸면서 문제가 발생한다.
new Function 문법으로 만든 함수 내부에서 외부 변수에 접근하려고 하면 userName은 이미 이름이 변경되었기 때문에 찾을 수 없게 되는 것이다.
- *압축기 : 스크립트에서 주석이나 여분의 공백 등을 없애 코드 크기를 줄여주는 특수한 프로그램
예를 들어, 함수 내부에 let userName라는 변수가 있으면 이 지역변수는 압축기에 의해 let a 등(짧은 이름)으로 대체되는데, 이때 userName 모두가 a로 교체된다.
userName은 지역변수이고, 함수 외부에선 함수 내부에 있는 변수에 접근할 수 없기 때문에 이렇게 해도 전혀 문제가 없다.
압축기는 단순히 변수를 찾아서 바꾸지 않고 코드 구조를 분석해 기존에 작성한 코드의 기능을 망가뜨리지 않으면서 영리하게 제 역할을 수행한다.
이러한 동작 방식 때문에 압축기가 동작한 이후엔, new Function으로 만든 함수 내부에서 외부 렉시컬 환경에 접근하려고 할 때 문제가 발생할 수 있다.
함수 내부에서 외부 변수에 접근하는 것은 아키텍처 관점에서도 좋지 않고 에러에 취약하므로, new Function으로 만든 함수에 무언갈 넘겨주고 싶다면 인자를 사용하자.
let func = new Function ([arg1, arg2, ...argN], functionBody);
new Function('a', 'b', 'return a + b'); // 기본 문법
new Function('a,b', 'return a + b'); // 쉼표로 구분
new Function('a , b', 'return a + b'); // 쉼표와 공백으로 구분