자바스크립트에서는 함수도 객체이다.
함수 정의
함수를 정의하는 방법에는 4가지가 있다.
- 함수 선언문
- 함수 표현식
- Function 생성자 함수
- 화살표 함수
함수 선언문
function add(x, y) {
return x + y;
}
console.dir(add);
consolg.log(add(3, 10));
console.dir 은 함수 객체의 프로퍼티까지 출력해준다.
함수 선언문은 함수 리터럴과는 달리 함수 이름을 생략할 수 없다는 걸 알고 있자.
함수 선언문은 표현식이 아닌 문이다.
표현식이 아닌 문은 변수에 할당할 수 없는데,
함수 선언문도 표현식이 아닌 문이므로 변수에 할당할 수 없다.
엥? 되는데요?
이게 되는 이유는 자바스크립트 엔진이 코드 문맥에 따라
함수리터럴을 표현식이 아닌 문인 함수 선언문으로 해석하는 경우와
표현식인 문인 함수 리터럴 표현식으로 해석하는 경우가 있기 때문이다.
함수선언문은 함수 이름을 생략할 수 없다는 점을 제외하면 함수 리터럴과 형태가 동일하다.
이건 이름이 있는 기명함수 리터럴은 함수 선언문으로 해석될 수도,
함수 리터럴 표현식으로 해석될수도 있다는 뜻이다.
책에서는 예시를 중괄호로 들었다.
중괄호는 블록문일 수도 있고 객체 리터럴일 수도 있다.
이는 코드의 문맥에 따라 {} 이 단독으로 존재하면 블록문으로, 피연산자로 사용되면 객체 리터럴로 해석한다.
비슷한 맥락으로 함수 이름이 있는 함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석하고,
함수 리터럴이 값으로 평가되어야하면 함수 리터럴 표현식으로 해석한다.
함수 선언문과 함수 리터럴 표현식은 함수를 생성하는 내부 동작이 약간 다르다.
function foo() { console.log('This is foo function.'); }
foo(); // This is foo function.
(function bar() { console.log('This is bar function.'); });
bar(); // ReferenceError: bar is not defined
단독으로 사용된 함수 리터럴(foo) 는 함수 선언문으로 해석되지만
그룹 연산자 내에 있는 함수 리터럴은 함수 리터럴 표현식으로 해석된다.
그룹 연산자의 피연산자는 값으로 평가될 수 있는 표현식이어야하기 때문이다.
왜 함수 선언문으로 생성된 foo 는 호출할 수 있고, 함수 리터럴 표현식으로 생성된 bar 는 호출할 수 없을까?
foo 는 자바스크립트 엔진이 암묵적으로 생성한 식별자다.
자바스크립트 엔진은 함수 선언문을 해석해 함수 객체를 생성한다.
자바스크립트 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고,
거기에 함수 객체를 할당한다.
var add = function add(x, y) {
return x + y;
}
console.log(add(1, 3)); // 4
var 뒤의 add 는 식별자로, function 키워드 뒤의 add 는 함수 이름이다.
우리가 호출하는것은 식별자를 이용하여 호출하는 것이다.
결론적으로 함수는 함수 이름으로 호출하는 것이 아닌 함수 객체를 식별자로 호출한다.
함수 표현식
계속 말해왔던 것처럼 자바스크립트의 함수는 객체 타입의 값이다.
자바스크립트의 함수는 값처럼 변수에 할당도 가능하고, 프로퍼티 값이 될수도, 배열의 요소가 될 수도 있다.
이렇게 값의 성질을 갖는 객체를 일급 객체 라고 한다.
자바스크립트 함수는 일급 객체이다.
함수가 일급 객체다 ?
이는 곧 함수를 값처럼 자유롭게 사용할 수 있다는 뜻이다.
아래 예시처럼 함수 리터럴로 생성한 함수 객체를 변수에 할당하는 것을 함수 표현식이라 한다.
var add = function (x, y) {
return x + y;
}
console.log(add(1, 3); // 4
함수 리터럴은 함수 이름을 생략할 수 있다.
일반적으로 함수 표현식의 함수 리터럴은 함수 이름을 생략한다.
함수 생성 시점 & 호이스팅
// 함수 참조
console.dir(sayHello);
console.dir(sayHi); // undefined
// 함수 호출
console.log(sayHello('치킨'));
console.log(sayHi('떡볶이')); // TypeError : sayHi is not a function;
// 함수 선언문
function sayHello(msg) {
return 'Hello' + msg;
}
// 함수 표현식
var sayHi = function(msg) {
return 'Hi' + msg;
}
위의 예시와 같이 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출 가능하나
함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.
이러한 점으로 미루어보아 우리는 두 함수의 생성 시점이 다르다 라는 것을 예측할 수 있다.
함수 선언문도 결국 선언문이기 때문에 런타임 이전에 자바스크립트 엔진에 의해 먼저 실행된다.
따라서 함수 선언문을 통해 함수를 정의하게 되면 런타임 이전에 함수 객체가 먼저 생성되는 것이다.
여기서 자바스크립트 엔진은
함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 생성된 함수 객체를 해당 식별자에 할당한다.
예전 포스팅에서도 한번 언급했었는데, 선언문이 위로 끌어진 것 마냥 동작하는걸 호이스팅이라고 한다.
함수의 경우 함수 호이스팅이라고 칭해진다.
이전에 봤었던 변수 호이스팅과의 차이점이 명확하다.
변수 호이스팅은 해당 변수가 선언된 이후 undefined 로 초기화 되지만,
함수 선언문을 통해 생성된 식별자는 함수 객체로 초기화 된다.
이렇게 설명이 된다면 왜 함수 표현식은 이전에 호출이 불가능한지 명확해진다.
함수 표현식은 결국 변수 선언 이후 값을 재할당 하기 때문에
sayHi 라는 변수는 런타임 이전에 undefind 로 초기화 되지만
값은 할당문이 실행되는 시점(런타임)에 평가되기 때문이다.
간단히 정리하면 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌 변수 호이스팅이 발생하는 것이다.
함수 호이스팅은 함수 호출 이전에 함수가 선언해야한다는 규칙을 무시하므로
JSON 을 창안한 더글라스 크락포드는 함수 선언문 대신 함수 표현식을 사용할 것을 권장한다.
Function 생성자 함수
자바스크립트가 기본으로 제공하는 빌트인 함수인Function 생성자 함수에
매개변수 목록과 함수 몸체를 문자열로 전달하면서
new 연산자와 함께 호출하면 함수 객체를 생성해 반환한다.
var add = new Function('x', 'y', 'return x + y');
console.log(add(1, 3)); // 4
사실 이 방식은 일반적이지도 않고, 옳은 방식도 아니다.
클로저를 생성하지 않는 등의 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작하기 때문이다.
화살표 함수
ES6에서 새로 도입된 것들 중 하나인 화살표 함수는 function 키워드 없이 화살표를 이용해 함수를 선언한다.
화살표 함수는 항상 이름이 없는 익명 함수이다.
const add = (x, y) =>x + y;
console.log(add(1, 3)); // 4
화살표 함수가 기존의 함수 선언문이나 함수 표현식을 완전히 대체하기 위해 디자인 된것은 아니다.
표현만 간략화되어 있는 것이 아닌 내부 동작 또한 간략화 되어있기 때문이다.
'Archive > Develop' 카테고리의 다른 글
[ 모던 자바스크립트 스터디 ] 스코프(Scope) (0) | 2022.09.18 |
---|---|
[ 모던 자바스크립트 스터디 ] 함수 호출 (0) | 2022.09.18 |
[ 모던 자바스크립트 스터디 ] 원시 값과 객체 비교 (0) | 2022.09.18 |
[ 모던 자바스크립트 스터디 ] 객체 (0) | 2022.09.17 |
[ 모던 자바스크립트 스터디 ] 옵셔널 체이닝 연산자 / null 병합 연산자 (0) | 2022.09.17 |