함수 정의
프로그래밍 언어의 함수는 일련의 과정울 문(statement)로 구현하고 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것이다.
함수 리터럴
자바스크립트의 함수는 객체의 타입
따라서 숫자 값을 숫자 리터럴로 생성하고 객체를 객체 리터럴로 생성하는 것처럼 함수도 함수 리터럴로 생성할 . 수 있다.
//변수에 함수 리터럴을 할당
var f = function add(x, y) {
return x + y;
}
자바스크립트에서 함수는 객체지만 일반 객체와는 다르다.
일반 객체는 호출할 . 수없지만 함수는 호출할 수 있다.
함수가 객체라는 사실은 다른 프로그래밍 언어와 구별되는 자바스크립트의 중요한 특징이다.
함수 선언문
function add(x,y) {
return x + y;
}
함수 선언문은 함수 리터럴과 형태가 동일함, 단 함수 리터럴은 함수 이름을 생략할 . 수있으나 함수 선언문은 함수 이름을 생략할 수 없다.
함수 선언문은 표현식이 아닌 문이다.
따라서 변수에 할당하여 사용할 수 없다.
표현식이 아닌 문이라는 말은 쉽게 설명하자면 값을 반환하지 않는다고 이해하면 된다.
하지만 다음 예제를 실행해보면 함수 선언문이 변수에 할당되는 것처럼 보인다.
var add = function add(x, y) {
return x + y;
}
이렇게 동작하는 이유는 자바스크립트 엔잔아 위의 구문을 함수 리터럴로 인식하기 때문이다.
함수 리터럴과 함수 선언문은 선언문의 경우 이름을 생략할 수 없을 뿐 기본 구조가 같기에 위의 구문은 함수 리터럴로 인식하여 작동한다.
아래의 예제를 보자.
function foo() {console.log('foo')};
foo();
(function bar() {console.log('bar')});
bar() // ReferenceError: bar is not defined
해당 예제에서 왜 foo는 호출되고 bar는 에러가 발생했을까?
그 원인을 살펴보자면 먼저 단독으로 사용된 foo는 함수 선언 문으로 해석된다.
하지만 그룹 연산자 () 내에 있는 bar는 함수 선언문으로 해석되지 않고 함수 리터럴 표현식으로 해석된다.
따라서 함수 리터럴인 bar는 식별자가 없는 것으로 취급되며, 함수 이름은 해당 함수의 몸체 외부에서 호출할 수 없으므로 bar라는 키워드로는 해당 함수를 호출할 수 없다.
그렇다면 foo와 같은 함수 선언문은 왜 해당 함수의 몸체 바깥에서 호출할 수 있을까?
그 이유는 자바스크립트 엔진이 암묵적으로 foo라는 식별자를 생성해주기 때문이다.
자바스크립트 엔진은 함수 선언문을 해석해 함수 객체를 생성한다.
이때 외부에서 함수를 호출해서 사용하기 위해 자바스크립트 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당한다.
따라서 함수는 함수 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출한다.
따라서 개발자가 작성한 함수 이름으로 함수를 호출하는 것이 아닌 자바스크립트가 암묵적으로 생성한 식별자(함수 이름과 동일하게 생성됨)가 함수 객체를 가리키며 이러한 식별자를 호출하여 사용하는 것이다.
함수 표현식
자바스크립트의 함수는 객체 타입의 값이다. 따라서 값처럼 변수에 할당할 수도 있다.
이처럼 값의 성질을 갖는 객체를 일급 객체라 한다.
자바스크립트의 함수는 일급 객체다. 일급 객체라는 것은 함수를 값처럼 자유롭게 사용할 수 있다는 것이다.
이처럼 함수를 변수에 저장하여 사용하는 것을 함수 표현식이라한다.
var add = function (x, y) {return x + y };
함수 리터럴의 함수 이름은 생략할 수 있으며 이러한 함수를 익명 함수라 한다.
이러한 점이 가능한 이유는 자바스크립트 엔진이 식별자를 함수 이름을 통해 생성할 필요가 없기 때문이다.
자바스크립트 엔진은 함수 선언문의 함수 이름으로 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당하므로 함수 표현식과 유사하게 동작하는 것처럼 보인다.
하지만 함수 선언문과 함수 표현식은 동일하게 동작하지는 않는다.
함수 선언식은 표현식이 아닌 문이고, 표현식은 표현식인 문이다. 이는 분명한 차이점이 있다.
함수 생성 시점과 호이스팅
console.dir(add); // f add(x, y)
console.dir(sub); // undefined
//함수 선언문
function add(x, y){
return x + y;
}
// 함수 표현식
var sub = function (x, y) {
return x - y;
}
위의 코드를 보면 함수 선언문으로 정의한 함수는 선언문 이전에 호출할 수 있다.
이러한 차이점이 발생하는 원인은 함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르기 때문이다.
함수 선언문의 경우 런타임 이전에 자바스크립트 엔진에서 함수 이름과 동일한 식별자를 암묵적으로 생성하고 생성된 함수 객체를 할당한다.
즉, 런타임 시점에는 함수 객체가 이미 생성되고 식별자에 할당이 완료된 상태다.
이처럼 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트 고유의 특징을 함수 호이스팅이라 한다.
함수 호이스팅과 변수 호이스팅에는 차이점이 있다.
변수 선언은 런타임 이전에 실행되어 undefined로 초기화되지만 변수 할당문의 값은 할당문이 실행되는 시점, 즉 런타임에 평가되므로 함수 표현식의 함수 리터럴도 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.
따라서 함수 표현식으로 함수를 정의하면 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다.
즉, 표현식을 선언 이전에 사용하면 undefined로 취급된다.
화살표 함수
ES6에서 도입된 함수는 function 키워드 대신 화살표를 사용해 간단하게 함수를 선언할 수 있다.
이때 화살표 함수는 항상 익명 함수로 정의한다.
화살표 함수는 기존의 함수보다 표현만 간략한 것이 아니라 내부 동작 또한 간략화되어 있다.
화살표 함수는 생성자 함수로 사용할 수 없고, 기존 함수와 this 바인딩 방식이 다르고, prototype 프로퍼티가 없으며 argments 객체를 생성하지 않는다.
(화살표 함수는 다음에 더 알아보자)
함수 호출
매개변수와 인수
자바스크립트에서 함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크하지 않는다.
즉, 함수를 호출할 때 매개변수의 개수만큼 인수를 전달하는 것이 일반적이지만 그렇지 않은 경우에도 에러가 발생하지 않는다.
인수가 부족해서인수가 할당되지 않은 매개변수의 값은 undefined이며 초가된 인수는 무시된다.
사실 초과된 인수가 그냥 버려지는 것은 아니며, 모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관된다.
매개변수의 최대 개수
ECMAScript 사양에서는 매개변수의 최대 개수에 대해 명시적으로 제한하고 있지 않다.
단 함수의 매개변수는 코드를 이해하는 데 방해되는 요소이므로 이상적인 매개변수는 0개이며 적을수록 좋다.
이상적인 함수는 한 가지 일만 해야하며 가급적 작게 만들어야 한다.
만약 많은 매개변수가 필요하다면 이를 객체로 인수를 전달하는 것이 유리하다.
참조에 의한 전달과 외부 상태의 변경
하나만 주의하면 된다. 함수의 매개변수로 원시 값이 아닌 객체 값을 전달할 때는 참조 값이 복사 후 전달되기 때문에 원본 값이 변경될 수 있다.
따라서 원본을 훼손하지 않고 사용하려면 깊은 복사를 통해 객체 값을 사용한다.
다양한 함수의 형태
즉시 실행 함수
함수의 정의와 동시에 즉시 호출되며 단 한 번만 호출된 후 다시 호출할 수 없다.
주로 익명 함수를 사용하는 것이 일반적이다.
(function () {
var a = 3;
var b = 5;
return a * b;
}());
즉시 실행 함수는 반드시 그룹 연산자(괄호)로 감싸야한다. 아니면 함수 선언문 취급되어 선언문의 이름이 없어 에러가 발생한다.(함수 선언문은 익명함수를 사용할 수 없다.)
즉시 실행 함수 내에 코드를 모아 두면 혹시 있을 수도 있는 변수나 함수 이름의 충돌을 방지할 수 있다.
중첩 함수
함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라 한다.
그리고 중첩 함수를 포함하는 함수는 외부 함수라 부른다.
중첩 함수는 외부 함수 내부에서만 호출할 . 수있다.
일반적으로 중첩 함수는 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
콜백 함수
함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 하며, 매개 변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 한다.
고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다.
다시 말해, 콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할수 있다.
순수 함수와 비순수 함수
순수 함수
- 함수평 프로그래밍에서 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 사이드 이펙트가 없는 함수
비순수 함수
- 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 사이드 이펙트가 있는 함수
순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환한다. 일반적으로 최소 하나 이상의 인수를 전달받는다.
인수를 전달 받지 않는 순수 함수는 항상 동일한 값을 반환하므로 상수와 마찬가지이기 때문이다.
반면 비순수 함수는 외부 상태에 영향을 주고받는다. 함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워진다.
따라서 함수 외부 상태의 변경을 지양하는 순수 함수를 사용하는 것이 좋다.
함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 불변성을 지향하는 프로그래밍 패러다임이다.
로직 내에 존재하는 조건문과 반복문을 제거해서 복잡성을 해결하며, 변수 사용을 억제하거나 생명주기를 최소화해서 상태 변경을 피해 오류를 최소화하는 것을 목표로 한다.
자바스크립트는 멀티 패러다임 언어이므로 객체지향 프로그래밍뿐만 아니라 함수형 프로그래밍 역시 적극적으로 활용하고 있다.
'Web > JS-deep-dive 학습' 카테고리의 다른 글
[15장] let, const 키워드와 블록 레벨 스코프 (0) | 2025.01.19 |
---|---|
[14장] 전역 변수의 문제점 (0) | 2025.01.18 |
[13장] 스코프 (0) | 2025.01.18 |
[11장] 원시 값과 객체의 비교 (0) | 2025.01.11 |
[10장] 객체 리터럴 (0) | 2025.01.08 |