핵심 정리
목차
1. 함수 선언문
2. 함수 표현식
3. 함수 호출
4. 인수 확인
5. 매개변수와 최대 개수
6. 반환문
7. 참조에 의한 전달과 외부 상태의 변경
8. 다양한 함수의 형태(즉시 실행, 콜백 함수)
9. 순수 함수과 비순수 함수(함수형 프로그래밍)
10. 이 책이 좋은 이유
12장 함수
프로그래밍 언어의 함수는 일련의 과정을 문(statement)으로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것입니다.
프로그래밍 언어의 함수도 입력을 받아서 출력을 내보냅니다.
// 함수 정의
function add(x, y) {
return x + y;
}
add(2, 5) // 함수 호출
함수 리터럴
// 변수에 함수 리터럴을 할당
var f = function add(x, y) {
return x + y;
};
함수 정의
함수 선언문
function add(x, y) {
return x + y;
}
함수 표현식
var add = function(x, y) {
return x + y;
};
Function 생성자 함수
var add = new Function('x', 'y', 'return x+y');
화살표 함수(ES6)
var add = (x,y) => x + y;
1. 함수 선언문
함수 선언문은 표현식이 아닌 문입니다. 크롬 개발자 도구의 콘솔에서 함수 선언문을 실행하면 완료 값 undefined가 출력됩니다다.
함수 선언문이 만약 표현식인 문이라면 완료 값 undefined 대신 표현식이 평가되어 생성된 함수가 출력되어야 합니다.
자바스크립트 엔진은 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당합니다.
var add = function(x, y) {
return x + y;
};
함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출합니다.
2. 함수 표현식
값의 성질을 갖는 객체를 일급 객체라 합니다. 자바스크립트의 함수는 일급 객체입니다. 함수가 일급 객체라는 것은 함수를 값처럼 자유롭게 사용할 수 있다는 의미입니다.
함수 표현식의 함수 리터럴은 함수 이름을 생략하는 것이 일반적입니다.
자바스크립트 엔진은 함수 선언문의 함수 이름으로 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당하므로 함수 표현식과 유사하게 동작하는 것처럼 보이지만 함수 선언문과 표현식이 정확히 동일하게 동작하지는 않습니다.
함수 선언문은 "표현식이 아닌 문"이고 함수 표현식은 "표현식인 문"이기 때문입니다.
바로 위 두개의 문은 생성 시점이 다릅니다.
함수 선언문 이전에 함수를 참조할 수 있으며 호출할 수도 있습니다.
함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 함수 호이스팅이라 합니다.
함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생합니다.
함수 호이스팅은 함수를 호출하기 전에 반드시 함수를 선언해야 한다는 당연한 규칙을 무시합니다. 이 같은 문제 때문에 더글라스 크락포드(JavaScript: The Good Parts 저자, JSON을 창안)는 함수 선언문 대신 함수 표현식을 사용하는 것을 권장합니다.
3. 함수 호출
매개변수와 인수
function add(x, y) { // x, y 매개변수
return x + y; // x, y 반환값
}
var result = add(2, 5); // 2, 5 인수
함수를 호출할 때 매개변수의 개수만큼 인수를 전달하는 것이 일반적이지만 그렇지 않은 경우에도 에러가 발생하지는 않습니다.
인수가 부족해서 인수가 할당되지 않는 매개변수의 값은 undefined입니다.
function add(x,y) {
return x + y;
}
console.log(add(2)); // NaN
매개변수보다 인수가 더 많은 경우 초과된 인수는 무시됩니다.
function add(x, y) {
return x + y;
}
console.log(add(2,5,10)); // 7
사실 초과된 인수가 그냥 버려지는 것은 아닙니다. 모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관됩니다.
function add(x, y) {
console.log(arguments);
return x + y;
}
console.log(add(2,5,10)); // 7
4. 인수 확인
자바스크립트는 함수의 매개변수가 코드상으로는 어떤 타입의 인수를 전달해야 하는지, 어떤 타입의 값을 반환하는지 명확하지 않습니다.
add('a', 'b');
add(2)
위와 같이 자바스크립트는 문법상 어떠한 문제도 없으므로 자바스크립트 엔진은 아무런 이의 제기없이 위 코드를 실행합니다.
이처럼 함수 내부에서 적절한 인수가 전달되었는지 확인하더라도 부적절한 호출을 사전에 방지할 수는 없고 에러는 런타임에 발생하게 됩니다.
따라서 타입스크립트와 같은 정적 타입을 선언할 수 있는 자바스크립트의 상위 확장을 도입해서 컴파일 시점에 부적절한 호출을 방지할 수 있게 하는 것도 하나의 방법입니다.
5. 매개변수와 최대 개수
ECMAScript 사양에서는 매개변수의 최대 개수에 대해 명시적으로 제한하고 있지는 않습니다.
그렇다면 매개변수는 최대 몇 개까지 사용하는 것이 좋을까요?
매개변수가 많아지면 함수를 호출할 때 전달해야 할 인수의 순서를 고려해야 합니다. 이는 함수의 사용법을 이해하기 어렵게 만들고 실수를 발생시킬 가능성을 높입니다.
함수의 매개변수는 코드를 이해하는 데 방해되는 요소이므로 이상적인 매개변수 개수는 0개이며 적을수록 좋습니다.
매개변수의 개수가 많다는 것은 함수가 여러 가지 일을 한다는 증거이므로 바람직하지 않습니다.
이상적인 함수는 한 가지 일만 해야 하며 가급적 작게 만들어야 합니다.
최대 3개 이상을 넘지 않을 것을 권장하며, 그 이상의 매개변수가 필요하다면 하나의 매개변수를 선언하고 객체를 인수로 전달하는 것이 유리합니다. jQuery의 ajax 메서드에 객체를 인수로 전달하는 예시
$.ajax({
method : 'POST',
url : '/user',
data : { id : 1, name : 'Lee'}
});
6. 반환문
반환문은 생략할 수 있습니다. 이때 함수는 함수 몸체의 마지막 문까지 실행한 후 암묵적으로 undefined를 반환합니다.
function foo() {
return;
}
console.log(foo()); // undefined
function foo() {
}
console.log(foo()); // undefined
7. 참조에 의한 전달과 외부 상태의 변경
원시 값은 값에 의한 전달, 객체는 참조에 의한 전달 방식으로 동작합니다.
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"}
원시 타입 인수는 값 자체가 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 그 값을 변경해도 원본은 훼손되지 않습니다.
어떠한 부수 효과도 발생하지 않습니다.
객체 타입 인수는 참조 값이 복사되어 매개변수에 전달도기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손됩니다.
원본 객체가 변경되는 부수 효과가 발생합니다.
함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워집니다. 이는 코드의 복잡성을 증가시키고 가독성을 해치는 원인이 됩니다.
이러한 문제의 해결 방법 중 하나는 객체를 불변 객체로 만들어 사용하는 것입니다.
객체의 복사본을 새롭게 생성하는 비용은 들지만 객체를 마치 원시 값처럼 변경 불가능한 값으로 동작하게 만드는 것입니다.
외부 상태를 변경하지 않고 외부 상태에 의존하지 않는 함수를 순수 함수라 합니다.
8. 다양한 함수의 형태
즉시 실행 함수
단 한 번만 호출되며 다시 호출할 수 없습니다.
// 익명 즉시 실행 함수
(function () {
var a = 3;
var b = 5;
return a * b;
}());
즉시 실행 함수는 함수 이름이 없는 익명 함수를 사용하는 것이 일반적이지만, 가명 즉시 실행 함수도 사용할 수 있습니다.
하지만 그룹 연산자(...) 내의 기명 함수는 함수 선언문이 아니라 함수 리터럴로 평가되며 함수 이름은 함수 몸체에서만 참조할 수 있는 식별자이므로 즉시 실행 함수를 다시 호출할 수는 없습니다.
그룹 연산자의 피연산자는 값으로 평가되므로 기명 또는 무명 함수를 그룹 연산자로 감싸면 함수 리터럴로 평가되어 함수 객체가 됩니다.
콜백 함수
함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 하며, 매개 변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 합니다.
고차 함수는 콜백 함수를 자신의 일부분으로 합성합니다. 고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다. 다시 말해, 콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있습니다.
function repeat(n, f) {
for(var i=0; i < n; i++) {
f(i);
}
}
var logAll = function(i) {
console.log(i);
};
repeat(5, logAll); // 0 1 2 3 4 5
var logOdds = function(i) {
if(i % 2) console.log(i);
};
repeat(5,, logAdds); // 1 3
콜백 함수가 고차 함수 내부에만 호출된다면 콜백 함수를 익명 함수 리터럴로 정의하면서 곧바로 고차 함수에 전달하는 것이 일반적이다.
repeat(5, function (i) {
if(i % 2) console.log(i);
});
9. 순수 함수과 비순수 함수
부수 효과가 없는 함수를 순수 함수라하고, 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수 효과가 있는 함수를 비순수 함수라고 합니다.
순수 함수는 인수의 불변성을 유지합니다. 즉 어떤 외부 상태에도 의존하지 않으며 외부 상태를 변경하지도 않는 함수입니다.
반대로 함수의 외부 상태에 따라 반환값이 달라지는 함수, 다시 말해 외부 상태에 의존하는 함수를 비순수 함수라고 합니다.
함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 불변성을 지향하는 프로그래밍 패러다임입니다. 로직 내에 존재하는 조건문과 반복문을 제거해서 복잡성을 해결하며, 변수 사용을 억제하거나 생명주기를 최소화해서 상태 변경을 피해 오류를 최소화하는 것을 목표로 합니다.
함수형 프로그래밍은 결국 순수 함수를 통해 부수 효과를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높이려는 노력의 일환이라 할 수 있습니다. 자바스크립트는 멀티 패러다임 언어이므로 객체지향 프로그래밍뿐만 아니라 함수형 프로그래밍을 적극적으로 활요하고 있습니다.
10. 이 책이 좋은 이유
자바스크립트 프로그래밍 언어에 대해서 정말 깊이 있게 잘 설명되어 있는 것은 기본이고, 어떤 게 좋은 코드인지 부연 설명과 Clean Code 책에서 보았던 내용들도 많이 언급되어 있어서 언어와 함께 좋은 코드를 같이 학습할 수 있어서 좋습니다!(내용이 많아서 두꺼운 건 비밀,,)
'Javascript' 카테고리의 다른 글
처음부터 시작하는 javascript deep dive(14.전역 변수의 문제점) (0) | 2024.01.21 |
---|---|
처음부터 시작하는 javascript deep dive(13.스코프) (2) | 2024.01.21 |
처음부터 시작하는 javascript deep dive(11.원시 값과 객체의 비교) (1) | 2024.01.07 |
처음부터 시작하는 javascript deep dive(10.객체 리터럴) (0) | 2024.01.02 |
처음부터 시작하는 javascript deep dive(09.타입 변환과 단축 평가) (2) | 2024.01.02 |