명명 된 함수 식을 사용하는 이유는 무엇입니까?


93

JavaScript에서 함수 표현을 수행하는 두 가지 방법이 있습니다.

명명 된 함수 식 (NFE) :

var boo = function boo () {
  alert(1);
};

익명 함수 표현식 :

var boo = function () {
  alert(1);
};

그리고 둘 다 boo();. 익명 함수를 사용해야하는 이유 /시기와 명명 된 함수 표현식을 사용해야하는시기를 알 수 없습니다. 그들 사이에는 어떤 차이가 있습니까?


답변:


86

익명 함수 표현식의 경우 함수는 익명입니다  . 말 그대로 이름이 없습니다. 할당하려는 변수에는 이름이 있지만 함수에는 없습니다. (업데이트 : 그것은 ES5를 통해 사실이었습니다. ES2015 [일명 ES6]부터, 종종 익명의 표현으로 생성 된 함수는 실제 이름을 얻습니다 [그러나 자동 식별자는 아닙니다], 계속 읽으십시오 ...)

이름이 유용합니다. 이름은 스택 추적, 호출 스택, 중단 점 목록 등에서 볼 수 있습니다. Names are a Good Thing ™.

(이전 버전의 IE [IE8 이하]에서 명명 된 함수 표현식을주의해야했습니다. 두 개의 완전히 별개의 함수 객체를 완전히 다른 두 시간에 실수로 만들었 기 때문입니다 [더 자세한 내용은 내 블로그 기사 Double take ]. IE8 [!!]을 지원합니다. 익명의 함수 표현식이나 함수 선언 을 고수하는 것이 가장 좋지만 명명 된 함수 표현식은 피하십시오.)

명명 된 함수 표현식에 대한 한 가지 중요한 점은 함수 본문 내에서 해당 함수 이름으로 범위 내 식별자를 생성한다는 것입니다.

var x = function example() {
    console.log(typeof example); // "function"
};
x();
console.log(typeof example);     // "undefined"

하지만 ES2015에서 많은 "익명"함수 표현식은 이름이있는 함수를 생성하며, 이는 컨텍스트에서 이름을 추론하는 데 매우 똑똑한 다양한 최신 JavaScript 엔진에 의해 선행되었습니다. ES2015에서 익명 함수 표현식은 이름이 boo. 그러나 ES2015 + 의미 체계를 사용하더라도 자동 식별자는 생성되지 않습니다.

var obj = {
    x: function() {
       console.log(typeof x);   // "undefined"
       console.log(obj.x.name); // "x"
    },
    y: function y() {
       console.log(typeof y);   // "function"
       console.log(obj.y.name); // "y"
    }
};
obj.x();
obj.y();

함수 이름에 대한 할당 은 사양의 다양한 작업에 사용되는 SetFunctionName 추상 작업으로 수행됩니다.

짧은 버전은 기본적으로 다음과 같이 할당 또는 초기화와 같은 것의 오른쪽에 익명 함수 표현식이 나타날 때마다 발생합니다.

var boo = function() { /*...*/ };

(또는 때문일 수 있습니다 let또는 const보다는 var) , 또는

var obj = {
    boo: function() { /*...*/ }
};

또는

doSomething({
    boo: function() { /*...*/ }
});

(마지막 두 개는 실제로 동일한 것입니다) , 결과 함수는 이름을 갖게됩니다 ( boo예에서).

중요하고 의도적 인 예외가 있습니다. 기존 개체의 속성에 할당 :

obj.boo = function() { /*...*/ }; // <== Does not get a name

이는 새로운 기능이 추가되는 과정에서 제기 된 정보 유출 우려 때문이었습니다. 여기 에 다른 질문에 대한 내 답변의 세부 사항 .


1
NFE를 사용하는 것이 여전히 구체적인 이점을 제공하는 두 곳 이상의 장소가 있다는 점은 주목할 가치가 있습니다. 첫째, new연산자 를 통해 생성자로 사용하려는 함수의 경우 (이러한 모든 함수 이름을 제공하면 .constructor디버깅 중에 속성이 도대체 무엇을 알아 내는지 더 유용 하게 만듭니다. 일부 객체는의 인스턴스입니다), 그리고 속성이나 변수에 먼저 할당되지 않고 함수에 직접 전달되는 함수 리터럴 (예 :)의 setTimeout(function () {/*do stuff*/});경우. (anonymous function)이름을 지정하여 도움 을 주지 않는 한 Chrome조차도 이를 표시합니다.
Mark Amery

4
@MarkAmery : "아직도 사실인가요? 저는 ...이 규칙에 대해 CTRL-F를 시도했지만 찾을 수 없었습니다." 오 예. :-) 규칙 집합을 정의하는 한곳에있는 것이 아니라 사양 전체에 흩어져 있습니다. "setFunctionName"을 검색하면됩니다. 위에 링크의 작은 하위 집합을 추가했지만 현재 ~ 29 개의 다른 위치에 표시됩니다. 나는 당신의 setTimeout예가에 대해 선언 된 공식적인 인수에서 이름을 가져 오지 않았다면 약간 놀랐 setTimeout을 것입니다. :-) 그러나 네, NFE는 해시를 만드는 오래된 브라우저를 다루지 않을 것이라는 것을 알고 있다면 확실히 유용합니다.
TJ Crowder

24

이름 지정 함수는 자신을 참조해야하는 경우에 유용합니다 (예 : 재귀 호출). 실제로 리터럴 함수 표현식을 다른 함수에 직접 인수로 전달하는 경우 해당 함수 표현식 은 이름이 지정되지 않는 한 ES5 엄격 모드에서 자신을 직접 참조 할 수 없습니다 .

예를 들어 다음 코드를 고려하십시오.

setTimeout(function sayMoo() {
    alert('MOO');
    setTimeout(sayMoo, 1000);
}, 1000);

전달 된 함수 표현식 setTimeout이 익명 이면이 코드를 이렇게 깔끔하게 작성하는 것은 불가능합니다 . setTimeout호출 하기 전에 변수에 할당해야합니다 . 이런 식으로 명명 된 함수 표현식을 사용하면 약간 더 짧고 깔끔합니다.

익명의 함수 표현식을 사용해도 이와 같은 코드를 작성하는 것이 역사적으로 가능했습니다 arguments.callee.

setTimeout(function () {
    alert('MOO');
    setTimeout(arguments.callee, 1000);
}, 1000);

...하지만 arguments.callee더 이상 사용되지 않으며 ES5 엄격 모드에서 완전히 금지됩니다. 따라서 MDN은 다음과 같이 조언합니다.

사용하지 마십시오 arguments.callee()중 하나에 의해 제공 기능 식의 이름을 또는 함수가 자신을 호출해야합니다 어디 함수 선언을 사용합니다.

(강조 내)


3

함수가 함수 표현식으로 지정되면 이름을 지정할 수 있습니다.

함수 내에서만 사용할 수 있습니다 (IE8- 제외).

var f = function sayHi(name) {
  alert( sayHi ); // Inside the function you can see the function code
};

alert( sayHi ); // (Error: undefined variable 'sayHi')

이 이름은 다른 변수에 기록 된 경우에도 신뢰할 수있는 재귀 함수 호출을위한 것입니다.

또한 NFE (Named Function Expression) 이름 Object.defineProperty(...)은 다음과 같은 방법으로 덮어 쓸 수 있습니다 .

var test = function sayHi(name) {
  Object.defineProperty(test, 'name', { value: 'foo', configurable: true });
  alert( test.name ); // foo
};

test();

참고 : 함수 선언으로이 작업을 수행 할 수 없습니다. 이 "특수"내부 함수 이름은 함수 표현식 구문에서만 지정됩니다.


2

당신은해야한다 항상 이름을 사용하십시오 이유 즉, 기능 식 :

  1. 재귀가 필요할 때 해당 함수의 이름을 사용할 수 있습니다.

  2. 익명 함수는 문제를 일으키는 함수의 이름을 볼 수 없기 때문에 디버깅 할 때 도움이되지 않습니다.

  3. 함수의 이름을 지정하지 않으면 나중에 그것이 무엇을하는지 이해하기가 더 어려워집니다. 이름을 지정하면 더 쉽게 이해할 수 있습니다.

var foo = function bar() {
    //some code...
};

foo();
bar(); // Error!

예를 들어, 이름 표시 줄은 함수 표현식 내에서 사용되기 때문에 외부 범위에서 선언되지 않습니다. 명명 된 함수 식을 사용하면 함수 식의 이름이 자체 범위 내에 포함됩니다.


1

.NET과 같은 더 이상 사용되지 않는 기능에 의존하지 않고 문제의 함수를 참조 할 수 있기를 원할 때 명명 된 함수 표현식을 사용하는 것이 더 좋습니다 arguments.callee.


3
그것은 대답 이라기보다는 코멘트에 가깝습니다. 정교함이 도움이 될 수 있습니다
vsync
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.