텐센트에서 인턴으로 면접을 했을 때 면접관이 저에게 이런 질문을 했던 기억이 납니다.
처음에는 함수 선언과 함수 표현식이라는 두 가지 선언 방법만 알고 있었는데 구체적인 차이점을 설명할 수 없었습니다. 최근에 우연히 이 주제에 관한 책을 보게 되었는데, 그 내용을 요약하고 싶었습니다.
ECMAScript에는 함수 객체를 생성하는 데 가장 일반적으로 사용되는 두 가지 방법, 즉 함수 표현식을 사용하거나 함수 선언을 사용하는 방법이 있습니다. 이와 관련하여 ECMAScript 사양에서는 함수 선언에는 항상 식별자(Identifier)가 있어야 하며, 이를 함수 이름이라고 부르며, 함수 표현식은 생략할 수 있음을 분명히 하고 있습니다.
함수 선언:
1. 새로운 Function 객체를 생성하고, FormalParameterList는 매개변수를 지정하고, FunctionBody는 함수 본문을 지정합니다. 현재 실행 중인 환경의 범위 체인을 범위로 사용합니다.
2. 값이 Result(1)인 현재 변수 객체에 대해 Identifier라는 속성을 생성합니다.
함수 표현:
(함수식은 익명함수식과 명명함수식으로 구분됩니다)
이름이 지정된 함수 표현식의 구문 분석 과정은 다음과 같습니다.
1. 새 개체 만들기
2. 스코프 체인의 상단에 Result(1)을 추가하세요
3. 새 Function 객체를 생성하고, FormalParameterList에 매개변수를 지정하고, FunctionBody에 함수 본문을 지정합니다. 현재 실행 중인 실행 환경의 범위 체인을 범위로 사용합니다.
4. Result(1)에 대해 Identifier라는 속성을 생성합니다. 해당 값은 Result(3)이고 읽기 전용이며 삭제할 수 없습니다.
5. 범위 체인에서 Result(1)
을 제거합니다.
6. 반품 결과(3)
공식 문서는 읽기가 매우 어렵습니다. 간단히 말해서 ECMAScript는 컨텍스트를 통해 둘을 구별합니다. function foo(){}가 할당 표현식의 일부인 경우 함수 표현식으로 간주됩니다. 그리고 function foo(){}가 함수 본문 내에 포함되어 있거나 프로그램의 최상위 수준에 있는 경우 함수 선언으로 구문 분석됩니다. 분명히 식별자가 생략되면 "표현식"은 표현식만 될 수 있습니다.
또 다른 상황이 있습니다:
이 경우도 괄호로 묶인 함수인 함수 표현식입니다. 문맥상 ()는 그룹화 연산자를 구성하며 그룹화 연산자는 표현식만 포함할 수 있습니다.
함수 선언과 함수 표현식의 유사점과 차이점에 대해 간략하게 이야기해 보겠습니다. 선언과 표현식의 동작에는 미묘하면서도 중요한 차이가 있습니다.
먼저 함수 선언을 분석하고 평가합니다. 그 전에 표현식을 분석하고 평가합니다. 선언이 소스 코드의 마지막 줄이더라도 동일한 범위의 첫 번째 표현식보다 먼저 평가됩니다. 예를 보면 이해하기가 더 쉽습니다. 다음 예에서는 경고 후에 fn 함수가 선언됩니다. 그러나 경고가 실행되면 fn이 이미 정의되어 있습니다.
간단히 요약하자면 차이점이 무엇인가요?
1. 선언은 항상 범위 시작 부분에서 구문 분석됩니다.
2. 표현식은 마주칠 때만 평가됩니다.
즉, 조건문을 통해 함수 선언을 제어하는 동작이 표준화되어 있지 않으므로 환경에 따라 다른 결과를 얻을 수 있습니다. 즉,
FunctionDeclaration은 Program 또는 FunctionBody에만 나타날 수 있습니다. 구문상 블록({ ... }) 내부(예: if, while 또는 for 문 내)에 나타날 수 없습니다. Block은 State만 포함할 수 있고 FunctionDeclaration과 같은 SourceElement는 포함할 수 없기 때문입니다.
반면에 생성 규칙을 자세히 살펴보면 표현식이 블록에 나타나는 유일한 방법은 이를 ExpressionStatement의 일부로 만드는 것임을 알 수 있습니다. 그러나 사양에는 ExpressionStatement가 키워드 function으로 시작할 수 없다고 명시되어 있습니다. 이것이 실제로 의미하는 바는 FunctionExpression이 명령문이나 블록에 나타날 수 없다는 것입니다(블록은 명령문으로 구성되어 있다는 점을 잊지 마십시오).
위의 제한으로 인해 함수가 블록에 나타날 때마다(위의 예와 같이) 실제로는 함수 선언이나 표현식이 아닌 구문 오류로 처리되어야 합니다.
그렇다면 함수 선언이나 함수 표현식은 언제 사용해야 할까요? 함수 선언은 "프로그램 코드"에만 나타날 수 있습니다. 즉, 해당 정의는 변수나 속성에 할당되거나 다음 함수 호출에서 매개 변수로 전달될 수 없습니다. 예를 들어 함수 선언의 허용된 사용은 foo(), bar() 및 local()이 모두 함수 선언 모드를 통해 선언되는 것입니다.