서적 모던 자바스크립드 Deep Dive 을 읽고 정리한 글입니다.
12장 함수
일급 객체인 자바스크립트 함수
일급 객체란 ?
다음 조건을 만족하는 객체를 일급 객체라고 한다.
- 무명의 리터럴로 생성 가능 ( 런타임에 생성이 가능 )
- 변수나 자료구조(객체,배열 등)에 저장할 수 있다.
- 함수의 매개 변수에 전달 가능.
- 함수의 반환값으로 사용 가능.
자바스크립트의 함수는 위 4개의 조건을 만족하므로 일급 객체이다.
함수가 일급 객체라는 말은 결국 함수 또한 객체라는 의미가 된다.
함수도 객체 처럼 값을 사용할 수 있는 곳( 변수 할당문, 객체 프로퍼트의 값, 배열 요소, 함수 호출의 인수, 반환문)이라면 어디서든지 리터럴로 정의 가능하며 런타임에 함수 객체로 평가 된다.
함수 리터럴
자바스크립트의 함수는 객체 타입의 값이므로 함수 리터럴로 생성이 가능하다.
// 변수에 함수 리터럴을 할당
var f = function add(x, y) {
return x + y;
};
리터럴 : 값을 생성하기 위한 표기법.
함수 리터럴도 평가되어 객체 값을 생성 한다.
함수 정의 방법
자바스크립트의 함수를 정의하는 방법은 4가지가 존재한다.
- 함수 선언문
- 함수 표현식
- Function 생성자 함수
- 화살표 함수(es6)
1. 함수 선언문
함수 리터럴과 동일한 형태이다. 다만 함수 리터럴은 함수 이름이 생략이 가능하나 선언문으로 생성하는 함수는 생략이 불가하다.
함수 선언문은 표현식이 아니므로 변수에 할당 할 수 없다.
function add(x,y){
return x+y;
}
2. 함수 표현식
var add = function (x,y){
return x+y;
}
함수 리터럴
함수 리터럴로 생성한 함수 객체를 변수에 할당 가능 하다. 리터럴 함수는 이름 생략 가능하다(익명함수)
함수 표현식은 변수에 할당되는 값이 함수 리터럴인 문이다.
3. Function 생성자 함수
var add = new Funcion('x','y','return x+y');
기본으로 제공하는 빌트인 함수 Function 생성자 함수에 매개변수 목록과 함수 몸체를 문자열로 전달하면서 new연산자와 함께 호출하면 함수 객체를 생성해서 반환.
Function 함수로 생성한 함수는 클로저로 생성하지 않는 등 함수 선언문이나 표현식으로 생성한 함수와 다르게 동작한다.
4. 화살표 함수
var add = (x,y)=>x,y;
익명함수로 정의된다.
화살표 함수는 기존의 함수 보다 내부 동작이 간략화 (기존 함수를 완전히 대체 불가하다)
생성자 함수로 사용할 수 없으며 기존 함수와 this 바인딩이 다르다. prototoype 프로퍼티가 없으며 arguments 객체를 생성하지 않는다.
함수 생성 시점과 호이스팅
함수 선언문과 표현식으로 정의한 함수들은 함수의 생성 시점이 각각 다르다.
함수 선언문으로 정의한 함수는 선언문 이전에 함수 호출이 가능하다.
표현식으로 정의한 함수는 표현식 이전에 호출 할 수 없다.
코드가 실행되는 런타임 이전에 자바스크립트 엔진은 모든 선언문 코드를 읽어서 선언한다 (호이스팅)
함수 선언문으로 생성한 함수는 런타임 이전에 객체가 먼저 생성된다.
런타임 시점에는 이미 함수 객체가 생성 되어 있고 할당까지 완료 된 상태이다.
변수의 호이스팅에서 var는 undefined로 초기화 되지만, 함수는 함수 객체로 초기화 된다.
따라서 var는 선언문 이전에 참조하면 undefined이지만 함수는 함수 호의스팅에 의해 호출이 가능하다.
함수 표현식은 변수 선언과 변수 할당을 한번에 기술한 축약 표현과 동일하게 동작한다.
변수 선언은 런타임 이전에 undefined로 초기화 되고 할당은 할당문이 실행되는 시점인 런타임에 평가 되므로 함수 리터럴도 런타임에 평가되어 함수 객체가 된다.
따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 아닌 변수 호이스팅이 발생 !
함수 호이스팅은 함수를 호출하기 전에 반드시 함수를 선언해야 한다는 당연한 규칙을 무시한다. 따라서 함수 선언문 대신 표현식을 사용할 것을 권장.
=> 요약
함수 호이스팅 : 함수가 선언되기 이전에 함수를 참조하여도, 선언문을 미리 읽어 선언해두는 js에 의해 함수 객체가 생성되는 함수 호이스팅이 발생.
선언문이 아닌 리터럴을 사용한 표현식으로 나타내면 함수 호이스팅이 아닌 변수 호이스팅이므로 선언문 이전에 함수 참조 불가 !
함수 호출
js 의 함수는 매개변수의 개수와 인수의 개수가 달라도 에러를 발생하지 않는다.
인수가 할당되지 않으면 매개 변수의 값은 undefined가 되고 매개변수 개수보다 더 많이 입력된 경우 초과된 인수는 무시한다.
function add(a = 0, b = 0, c = 0) {
return a + b + c;
}
console.log(add(1, 2, 3)); // 6
console.log(add(1, 2)); // 3
console.log(add(1)); // 1
console.log(add()); // 0
참조에 의한 전달과 외부 상태의 변경
매개변수도 함수 몸체 내부에서 변수와 동일하게 취급된다.
매개변수 또한 타입에 따라 값에 의한 전달(원시 값), 참조에 의한 전달(객체) 방식을 그대로 따른다.
// 매개변수 primitive는 원시값을 전달받고, 매개변수 obj는 객체를 전달받는다.
function changeVal(primitive, obj) {
primitive += 100;
obj.name = 'Kim';
}
// 외부 상태
var num = 100;
var person = { name: 'Lee' };
console.log(num); // 100
console.log(person); // {name: "Lee"}
// 원시값은 값 자체가 복사되어 전달되고 객체는 참조값이 복사되어 전달된다.
changeVal(num, person);
// 원시값은 원본이 훼손되지 않는다.
console.log(num); // 100
// 객체는 원본이 훼손된다.
console.log(person); // {name: "Kim"}
원시 타입인 인수는 값 자체가 복사되어 매개변수에 전달. 함수 몸체에서 그 값을 변경(재할당을 통한 교체)해도 원본 훼손 되지 않음.
객체 타입 인수는 참조값이 복사되어 전달. 참조값을 통해 객체를 변경할 경우 원본이 훼손된다.
콜백 함수와 고차 함수
콜백 함수:함수의 배개변수를 통해 다른 함수의 내부로 전달되는 함수
고차 함수: 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수
고차 함수는 콜백 함수를 자신의 일부분으로 합성한다.
// 외부에서 전달받은 f를 n만큼 반복 호출한다
function repeat(n, f) {
for (var i = 0; i < n; i++) {
f(i); // i를 전달하면서 f를 호출
}
}
var logAll = function (i) {
console.log(i);
};
// 반복 호출할 함수를 인수로 전달한다.
repeat(5, logAll); // 0 1 2 3 4
var logOdds = function (i) {
if (i % 2) console.log(i);
};
// 반복 호출할 함수를 인수로 전달한다.
repeat(5, logOdds); // 1 3
고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출.
콜백함수는 고차함수에 의해 호출되며 이때 고차함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
따라서 고차함수에 콜백 함수를 전달할 때 콜백함수를 호출하지 않고 함수 자체를 전달해야 한다.
콜백함수로서 전달된 함수 리터럴은 고차 함수가 호출될 때 마다 평가되어 함수 객체를 생성한다. 따라서 콜백 함수를 전달 받는 함수가 자주 호출된다면 함수으 외부에서 콜백함수를 정의한 후 함수 참조를 고차함수에 전달하는 편이 효율적이다.
// 익명 함수 리터럴을 콜백 함수로 고차 함수에 전달한다.
// 익명 함수 리터럴은 repeat 함수를 호출할 때마다 평가되어 함수 객체를 생성한다.
repeat(5, function (i) {
if (i % 2) console.log(i);
}); // 1 3
// logOdds 함수는 단 한 번만 생성된다.
var logOdds = function (i) {
if (i % 2) console.log(i);
};
// 고차 함수에 함수 참조를 전달한다.
repeat(5, logOdds); // 1 3
logOdds는 함수가 단 한번 생성되지만 그 위의 익명 함수 리터럴로 정의한 예제는 고차함수가 호출될 때 마다 콜백 함수가 생성된다.