함수는 자바스크립트에서 가장 중요한 핵심 개념
- 스코프, 실행컨텍스트, 클로저, 생성자 함수에 의한 객체 생성, 메서드, this, 프로토타입 모듈화 등 모두 함수와 연관
- 함수는 일련의 과정을 문으로 구현하고 코드블록으로 감싸서 하나의 실행 단위로 정의한 것이다.
- 함수의 구성요소 : 매개변수, 인수, 반환값
- 함수 정의를 통해 생성하고 함수를 호출한다. 함수를 호출하면 반환값 리턴
function add(x,y) {
return x+y;
}
var result = add(2,5);
console.log(result);
함수를 사용하는 이유
- 함수사용시 코드의 재사용이 매우 유용하다.
- 유지보수의 편의성 증가, 코드의 신뢰성 증가
- 적절한 함수이름을 붙여 역할을 파악을 쉽게 해준다. → 코드의 가독성 향상
함수 리터럴
- 자바스크립트의 함수는 객체 타입의 값이다. 함수도 함수 리터럴로 생성할 수 있다.
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;
함수 선언문
- 함수 이름 생략할 수 없다.
- 표현식이 아닌 문이여서 변수에 할당이 불가하다. 하지만 함수 선언문이 변수에 할당되는 것처럼 보일때가 있다. → 이는 함수 선언문이 아닌 함수 리터럴 표현식으로 해석된 것이다. (두개는 형태가 동일하기 때문)
- 기명함수 리터럴을 단독으로 사용하면 함수 선언문으로 해석
- 기명함수 리터럴을 피연산자로 사용하면 함수 리터럴 표현식으로 해석 (함수이름 생략 가능)
- 자바스크립트 엔진은 함수 선언문으로 생성된 함수를 호출하기 위해 “함수 이름과 동일한 이름의 식별자를 암묵적으로 생성”하고 거기에 함수 객체를 할당한다.
- 함수는 함수 이름으로 호출하는 듯하지만 함수 객체를 가리키는 식별자로 호출한다.
함수 표현식
- 자바스크립트 함수는 일급 객체다.→ 함수를 값처럼 자유롭게 사용 가능
- 함수리터럴로 생성한 함수 객체를 변수에 할당하는 방식을 함수 표현식이라 한다.
- 함수리터럴의 함수 이름 생략 가능(익명 함수)
- 함수 선언문은 “표현식이 아닌 문”
함수 표현식은 “표현식인 문”으로 중요한 차이가 있다.
함수 생성 시점과 함수 호이스팅
console.dir(add);
console.dir(sub);
console.log(add(2,5));
console.log(sub(2,5));
function add(x,y) {
return x+y;
}
var sub = function (x,y) {
return x-y;
};
- 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있지만 함수 표현식으로 정의한 함수는 함수 표현식 이전에 호출할 수 없다.
- 함수 선언문과 표현식으로 정의한 함수의 생성시점은 다르다.
- 선언문 : 런타임 이전에 먼저 함수 객체가 생성되고 식별자에 할당된다. ⇒ 선언문은 이전에 함수 호출 가능. "함수 호이스팅"
- 표현식 : 런타임에 평가되어 할당문이 실행되는 시점에 평가되어 함수 객체가 된다. 함수 호이스팅이 아닌 변수 호이스팅이 발생해 변수가 undefined로 평가된다.
- 호이스팅이란 var 선언문이나 function 선언문 등 모든 선언문이 해당 Scope의 선두로 옮겨진 것처럼 동작하는 특성을 말한다. 즉, 자바스크립트는 모든 선언문(var, let, const, function, function, class)이 선언되기 이전에 참조 가능하다.
Function 생성자 함수
- 기본 제공하는 빌트인 함수 Function 생성자 함수에 매개변수 목록과 함수 몸체를 문자열로 전달하면서 new 연산자와 함께 호출하면 함수 객체를 생성해서 반환한다.
- 생성자 함수 : 객체를 생성하는 함수를 말한다.
- 이 방식은 일반적이지 않으며 바람직한 방법도 아니다. (클로저생성하지 않음, 함수 선언문이나 함수 표현식과 다르게 동작)
화살표 함수
- function 키워드 대신 화살표 ⇒ 를 사용해 간략한 방법으로 함수를 선언할 수 있다.
- 익명함수로 정의한다.
const add = (x,y) => x+y;
add(2,5);
- 기존 함수와 this 바인딩 방식이 다르고 prototype프로퍼티가 없으며 arguments 객체를 생성하지 않는다.
함수 호출
매개변수와 인수
- 함수를 실행하기 위해 필요한 값을 매개변수(parameter)를 통해 인수(argument)를 전달한다.
- 인수는 값으로 평가될 수 있는 표현식이어야 한다. 인수는 함수를 호출할때 지정하며, 개수와 타입에 제한이 없다.
- 매개변수는 함수를 정의할 때 선언한다. 함수 몸체 내부에서 변수와 동일하게 취급된다. 외부에서는 참조할 수 없다. (매개변수의 스코프는 함수 내부)
- 매개변수보다 인수의 개수가 부족해도 에러는 나지 않는 대신 인수가 할당되지 않는 매개변수의 값은 undefined 이다. 초과된 경우는 무시
인수 확인
- 자바스크립트 함수는 매개변수와 인수의 개수가 일치하는지 확인하지 않는다.
- 자바스크립트는 동적 타입 언어다. ⇒ 매개변수의 타입을 사전에 지정할 수 없다.
- 함수를 정의할때 적절한 인수가 전달되었는지 조건으로 확인할 필요가 있다.
- 정적 타입을 선언할 수 있는 타입스크립트를 사용해 부적절한 호출을 방지할 수 있다.
- 단축평가나 매개변수 기본값을 사용하면 인수 체크 및 초기화를 간소화 할 수 있다.
매개변수의 최대 개수
- 매개변수의 최대 개수에 대해 명시적으로 제한하고 있진 않지만 가급적 작게 만드는게 좋다. *3개 이상을 넘지 않는 것을 권장한다.
- 매개변수는 순서에 의미가 있다.
- 객체를 인수로 사용하는 경우에는 프로퍼티 키만 정확히 지정하면 매개변수의 순서를 신경 쓰지 않아도 된다.
- 다만, 외부에서 함수 내부로 전달한 객체를 내부에서 변경하면 외부의 객체가 변경되는 side effect가 발생하는 것을 주의한다.
반환문
- 함수는 return 키워드와 표현식(반환값)으로 이뤄진 반환문을 사용해 실행 결과를 함수 외부로 반환할 수 있다.
- 2가지 역할
- 함수의 실행을 중단하고 함수 몸체를 빠져나간다. 뒤에 코드가 더 있어도 무시된다.
- return 키워드 뒤에 오는 표현식을 평가해 반환한다. 반환문을 생략하면 암묵적으로 undefined가 반환된다.
참조에 의한 전달과 외부 상태의 변경
- 매개변수를 통해 전달받은 원시 타입 인수와 객체 타입 인수를 함수 몸체에서 변경시
- 원시 타입 인수는 원시값의 원본을 변경하는 어떠한 부수 효과도 발생하지 않는다.
- 객체 타입 인수는 참조값이 복사되어 매개변수에 전달되어 함수 몸체에서 변경할 경우 원본 객체가 변경되는 부수 효과가 발생한다.
- 원본 객체의 변경을 추적하는 것은 어려워진다. ⇒ 객체를 “불변 객체”로 만들어서 사용한다.
- 객체를 원시값처럼 변경 불가능한 값으로 동작하게 만드는 것
- 깊은 복사를 통해 새로운 객체를 생성하고 재할당을 통해 교체한다.
다양한 함수의 형태
즉시 실행 함수
- 함수의 정의와 동시에 즉시 호출되는 함수를 즉시 실행 함수(IIFE)라고 한다.
- 단 한번만 호출되고 다시 호출할 수 없다.
(function () {
var a = 3;
var b = 5;
return a * b;
}());
- 즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있고 인수를 전달할 수도 있다.
- 즉시 실행 함수 내에 코드를 모아 두면 변수나 함수 이름의 충돌을 방지할 수 있다. (전역 변수의 사용을 억제하는 방법)
재귀 함수
- 함수가 자기 자신을 호출하는 것을 재귀 호출이라 한다. 재귀 함수는 자기 자신을 호출하는 것을 말한다.
- 반복 처리를 위해서 사용된다.
function factorial(n) {
if (n < 2) return 1;
return factorial(n - 1) * n;
}
console.log(factorial(0));
console.log(factorial(1));
console.log(factorial(2));
console.log(factorial(3));
console.log(factorial(4));
console.log(factorial(5));
console.log(factorial(6));
- 재귀함수는 자신을 마한 재귀 호출할 수 있어 반드시 탈출 조건을 만들어야 한다.
- 무한 반복과 스택 오버플로 에러를 발생할 수 있어 주의해서 사용해야 한다.
중첩 함수
- 함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라 한다.
- 중첩 함수를 포함하는 함수는 외부 함수라 한다.
- 중첩함수는 외부함수 내에서만 호출할 수 있고 스코프, 클로저와 깊은 관련이 있다.
콜백 함수
- 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라고 한다.
- 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 한다.
- 콜백 함수는 함수 외부에서 고차 함수 내부로 주입하기 때문에 자유롭게 교체할 수 있는 장점(고차 함수는 콜백 함수를 자신의 일부분으로 합성한다.)
- 콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
- 콜백 함수를 전달받는 함수가 자주 호출된다면 함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차 함수에 전달하는 편이 효율적이다.
function repeat(n,f) {
for(var i=0;i<n;i++) f(i);
}
var logOdds = function (i) {
if(i%2) console.log(i);
};
repeat(5, logOdds);
- 비동기 처리, 이벤트 처리, 고차함수에 중요하게 사용된다.
순수함수와 비순수함수
- 함수형 프로그래밍에서는 어떤 외부 상태에 의존하지도 않고 변경하지도 않는 부수효과가 없는 함수를 “순수 함수”라 한다.
- 이와 달리 외부 상태에 의존하고 상태를 변경하는 부수효과가 있는 함수를 “비순수 함수”라 한다.
<순수함수>
- 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수
- 순수함수는 일반적으로 최소 하나 이상의 인수를 전달받는다. 순수함수는 인수의 불변성을 유지한다.
- 어떤 외부 상태에도 의존하지 않으며 함수의 외부 상태를 변경하지 않는다.
<비순수함수>
- 외부 상태에 의존하고 상태도 변경할 수 있는 함수
- 함수 내부에서 외부 상태를 직접 참조하지 않더라도 매개변수를 통해 객체를 전달받으면 비순수 함수가 된다.
- 외부 상태를 변경하면 상태 변화를 추적하기 어려워진다. 복잡성 증가
<함수형 프로그래밍>
- 함수형 프로그래밍은 순수함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 “불변성”을 지향하는 프로그래밍 패러다임이다.
- 순수 함수를 통해 부수 효과를 최대한 피하고 프로그래밍의 안전성을 높인다.