12. 함수
in 모던자바스크립트
함수란?
- 일련의 과정을 문으로 구현하고, 코드 블록으로 감싸서 하나의 실행 단위로 정의한 것
- 매개 변수 (parameter) : 함수 내부로 입력을 전달받는 변수
- 인수 (argument) : 입력
- 반환값 (return value) : 출력
- 함수는 객체 타입의 값이므로 식별자를 사용한다.
// 함수를 정의하여 생성한다.
function add(x,y){ //x,y 매개변수(parameter), add : 식별자
return x + y //반환값
}
add(2,5) //2,5 인수(argument)
// 함수 호출 : 코드 블록에 담긴 문들이 일괄적으로 실행되고, 실행결과 (반환값)을 반환
// 반환값 (5)
함수를 사용하는 이유
- 함수는 필요할 때 여러번 호출 가능 > 실행시점을 개발자가 결정할 수 있고, 코드의 재사용 가능
- 코드의 중복을 억제, 재사용성 높이는 함수 > 유지보수의 편의성 up, 실수를 줄여 코드의 신뢰성 up
- 함수 이름은 함수의 역할을 파악할 수 있도록 > 코드 가독성 up
함수 리터럴
- 함수는 함수 리터럴로 생성
var f = function add(x,y){
return x + y;
};
함수 이름
- 식별자이므로 식별자 네이킹 규칙 준수해야 한다.
- 함수 몸체 내에서만 참조할 수 있는 식별자
- 생략 가능하다. 이름 있는 함수 = 기명함수, 없는 함수 = 익명함수
매개변수 목록
- 0개이상, 쉼표로 구분
- 매개변수에는 함수를 호출할 때 지정한 인수가 순서대로 할당 (순서에 의미가 있다.)
- 매개변수는 함수의 본체 내에서 변수와 동일하게 취급 > 식별자 네이밍 규칙 준수해야한다.
함수 몸체
- 함수가 호출되었을 때 일괄적으로 실행될 문들을 하나의 실행단위로 정의한 코드 블록
함수 호출에 의해 실행된다.
- 함수 리터럴도 평가되어 값을 생성하며, 이 값은 객체다. 즉. 함수는 객체다.
- but 일반 객체는 호출 x, 함수는 호출할 수 있다
- 일반 객체에는 함는 함수만의 고유한 프로퍼티를 갖는다.
- 다른 언어에서 함수는 객체가 아니므로 함수가 객체라는 사실을 Js의 중요한 특징
함수 정의
- 함수를 호출하기 전에 인수로 전달받은 매개변수와 실행할 문들, 반환값을 지정하는 것
- 정의된 함수는 js 엔진에 의해 평가되어 함수 객체가 된다.
// 함수 선언문
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");
// 화살표 함수
var add = (x, y) => x + y;
변수는 선언한다. 함수는 정의한다.
- 함수 선언문이 평가되면 식별자가 암묵적으로 생성되고 함수 객체가 할당된다.
함수 선언문
function add(x, y) {
return x + y;
}
console.dir(add) // f add(x,y)
// console.dir은 함수 객체의 프로퍼티까지 출력
// node.js 환경에서는 console.log와 기능이 같다
- 함수 선언문은 함수 리터럴과 형태가 동일하다.
- but 함수 리터럴은 이름을 생랼할 수 있지만 선언문은 이름을 생략할 수 없다.
- 함수 선언식은 표현식이 아닌 문
- 따라서 변수에 할당할 수 없다.
// 함수 선언문은 표현식이 아니기 때문에 변수에 할당할 수 없지만,
// 할당된 것처럼 보인다..?
// why?
var fn = function add(x, y) {
return x + y;
}
console.log(fn) //[Function: add]
- 이런 이유는 js 엔진이 코드의 문맥에 따라 동일한 함수 리터럴을 표현식인 함수리터럴, 표현식이 아닌 함수리터럴로 해석하는 경우가 있기 때문.
- 함수 선언문은 이름을 생략 x 빼고 함수 리터럴과 유사, 기명 함수 리터럴은 함수 선언문이나 함수 리터럴 표현식으로 해석될 가능성이 있다는 의미
- 뭔소리? {} 도 코드블록이냐, 객체냐 Js엔진이 그때그때 문맥에 따라 해석해
- 기명 함수 리터럴도 중의적 > 코드의 문맥에 따라 해석이 달라질 수 있다.
- js 엔진은 기명 함수 리터럴을 단독으로 사용하면(값으로x, 피연산자x) 함수 선언문으로 해석
- 함수 리터럴이 값으로 평가되는 경우면 (변수에 함수 리터럴 할당, 피연산자) 함수 리터럴 표현식으로 해석
- 두 경우 모두 함수가 생성되는 것은 동일하지만, 함수를 생성하는 내부 동작의 차이가 있다.
// 함수 선언문으로 해석
function foo() {
console.log("foo");
}
foo();
//foo
// 값으로 평가 > 함수 리터럴로 해석
// 함수 이름은 함수 몸체 외부에서는 호출 불가
// 따라서 에러
(function bar() {
console.log("bar");
})
bar(); //ReferenceError: bar is not defined
- 함수 선언문은 암묵적으로 함수이름과 같은 이름의 식별자를 만들어 함수의 참조값을 가르키게 한다.
- 함수 리터럴의 경우 함수를 가리키는 식별자가 존재하지 않기 때문에 함수를 호출할 수 없다.
- js 엔진은 생성된 함수를 호출하기 위해 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 거기에 함수 객체를 할당
- 함수는 함수 이름으로 호출하는 것이 아닌 함수 객체를 가리키는 식별자로 호출
함수 표현식
- 일급 객체 : 값의 성질을 갖는 객체
- js에서 함수는 일급 객체다 > 함수를 값처럼 자유롭게 사용할 수 있다
- 함수는 일급객체이므로 함수 리터럴로 생성한 함수 객체를 변수에 할당 할 수 있으며, 이러한 정의 방식을 함수 표현식
- 함수 리터럴의 함수 이름은 생략 가능하다. > 익명 함수
- 함수 표현식의 함수 리터럴은 함수 이름을 생략하는 것이 일반적이다.
- 변수에 할당되는 값이 함수 리터럴인 문
var foo = function (x, y) {
return x + y;
};
console.log(foo(2, 5)); //7
var add = function foo(x, y) {
return x + y;
};
// 함수 이름은 함수 몸체 내부에서만 유효한 식별자이다.
console.log(foo(2, 5)); //ReferenceError: foo is not defined
console.log(add(2, 5)); //7
- 함수 선언문과 함수 표현식이 유사하게 동작하는 것 같아 보이지만 정확히 동일하게 동작하지 않는다.
- 함수 선언문 : 표현식이 아닌 문
- 함수 표현식 : 표현식인 문
함수 생성 시점과 함수 호이스팅
console.log(add(2, 5)); //7
console.log(sub(2, 5)); //TypeError: sub is not a function
function add(x, y) {
return x + y;
}
var sub = function (x, y) {
return x - y;
};
- 함수 선언문으로 정의한 함수와 함수 표현식으로 정의한 함수의 생성 시점이 다르다.
- 함수 선언문은 런타임 이전에 함수 객체가 먼저 생성된다.
- js엔진이 암묵적으로 함수 이름과 동일한 식별자를 생성하여 함수 객체를 할당한다.
- 즉, 런타임중에는 이미 함수 선언문의 함수 객체, 함수 이름과 동일한 식별자까지 세팅 완료
- 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 것 > 함수 호이스팅
- 함수 표현식 = 변수 선언문(런타임 이전) + 변수 할당문(런타임중)
- 함수 표현식의 함수 리터럴은 할당문이 실행되는 시점에 평가되어 함수 객체가 된다.
- 따라서 함수 표현식으로 함수를 정의하면, 함수 호이스팅이 발생하는 것이 아니라 변수 호이스팅이 발생한다.
- 함수 호이스팅은 함수를 호출하기 전에 반드시 함수를 선언해야 한다는 규칙을 무시 > 함수 표현식 지향!
Function 생성자 함수
- js 가 기본 제공하는 빌트인 함수 Function 생성자 함수에, 매개변수 목록과 함수 몸체를 문자열로 전달 + new 연산자와 함께 호출 ⇒ 함수 객체 생성하여 반환. (new 연산자 없이 호출해도 결과는 동일)
- 생성자 함수 : 객체를 생성하는 함수
var add = new Function("x", "y", "return x+y");
// Function 생성자 함수로 함수를 생성하는 것은 바람직하지 X
// 클로저를 생성하지 않는다.
// 함수 선언문이나 함수 표현식으로 생성한 함수와 다르게 동작한다.
console.log(add(2, 5)); //7
화살표 함수
- ES6에 도입. function 키워드 대신 화살표를 사용해 함수를 선언
- 화살표 함수는 항상 익명함수로 정의
var add = (x, y) => x + y;
console.log(add(2, 5)); //7
- 화살표 함수는 함수 선언문, 표현식을 완전히 대체하기 위해 디자인x → 내부 동작도 간략화되어있다.
- 생성자 함수로 사용할 수 없다.
- 기존 함수와 this 바인딩 방식이 다르다.
- prototype 프로퍼티가 없다.
- arguments 객체를 생성하지 않는다.
함수 호출
- 함수를 가리키는 식별자와 한 쌍의 소괄호(함수 호출 연산자)로 호출
- 0개 이상의 인수를 쉼표로 구분하여 나열
- 함수를 호출하면 현재의 실행 흐름 중단 > 호출된 함수로 실행 흐름 옮긴다.
- 이때 매개변수에 인수가 순서대로 할당, 함수 문체의 문들 실행
매개변수와 인수
- 함수 실행을 위해 필요한 값을 함수 외부에서 내부로 전달 > 매개변수를 통해 인수를 전달
- 인수는 값으로 평가될 수 있는 표현식이어야 한다.
- 인수는 함수를 호출할 때 지정, 개수와 타입에 제한이 없다.
function add(x,y){
return x + y;
}
var result = add(1,2); //인자 1,2를 함수 add의 파라미터 x, y에 순서대로 할당.
- 매개변수는 함수를 정의할 때 선언, 함수 몸체 내부의 변수와 동일하게 취급
- 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개변수가 생성되고 일반 변수와 마찬가지로 undefined로 초기화된 이후 인수가 순서대로 할당
- 함수가 호출될 때마다 매개변수는 이와 같은 단계를 거친다.
- 매개변수는 함수 몸체 내부에서만 참조 가능, 함수 몸체 외부에서는 참조 불가능
매개변수의 스코프(유효 범위)는 함수 내부
- 함수는 매개변수의 개수와 인수의 개수가 일치하는지 체크x
- 함수를 호출할 때 매개변수의 개수만큼 인수를 전달하는 것이 일반적이지만 그렇지 않아도 에러 x
- 인수가 부족해서 할당되지 않은 매개변수의 값은 undefined
function add(x, y) {
console.log(arguments); //[Arguments] { '0': 2, '1': 5, '2': 10 }
return x + y;
}
console.log(x); //ReferenceError: x is not defined
console.log(add(2)); //undefiend
console.log(add(2,5,10)); //7
- 매개변수보다 인수가 더 많은 경우, 초과된 인수는 무시
- 사실 버려지는 것은 아니다. 모든 인수는 암묵적으로 arguments 객체의 프로퍼티로 보관된다.
- arguments 객체는 함수를 정의할 때 매개변수 개수를 확정할 수 없는 가변 인자 함수를 구현할 때 유용
인수 확인
function add(x, y) {
return x + y;
}
// 함수를 정의한 사람은 숫자가 들어오길 기대하지 않았을까?
// but 코드상으로는 어떤 타입의 인수를 전달하고, 어떤 타입의 값을 반환하는지 안나와있다.
console.log(add("a", "b")); //ab
- 위의 상황이 왜 발생했을까?
- js 함수는 매개변수와 인수의 개수가 일치하는지 확인하지 않는다.
- js는 동적 타입 언어다. 따라서 js 함수는 매개변수의 타입을 사전에 지정할 수 없다.
- 따라서, 함수를 정의할 때 적절한 인수가 전달되었는지 확인할 필요가 없다.
function add(x, y) {
if (typeof x !== "number" || typeof y !== "number") {
throw new TypeError("인수는 모두 숫자!");
}
return x + y;
}
console.log(add("a", "b")); //TypeError: 인수는 모두 숫자!
- 위와 같이 적절한 인수가 전달됐는지 확인하더라도 부적절한 호출을 사전에 막을 순 x, 에러는 런타입에 발생
- 따라서 타입스크립트와 같이 정적타입을 선언하여 컴파일 시점에 부적절한 호출을 방지하는 것도 한 방법
function add(a, b) {
a = a || 0; // 인수가 전달되지 않을 경우, 단축평가를 사용해 매개변수에 기본값을 할당할 수도 있다.
b = b || 0;
return a + b;
}
console.log(add(7)); //7
console.log(add()); //0
function add(a = 0, b = 0) { // 매개변수 기본값 사용
return a + b;
}
console.log(add(7)); //7
console.log(add()); //0
- es6에 도입된 매개변수 기본값을 사용하면 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다.
- 매개변수 기본값 : 매개변수에 인수를 전달하지 않았을 경우와 undefined를 전달할 경우에만 유효
매개변수와 최대 개수
- ECMAScript 사양에서는 매개변수의 최대 개수에 대해 명시적으로 제한 X
- 물리적 한계는 존재하므로 적절히 사용 > 그렇다면 최대 몇개가 좋을까?
- 매개변수는 순서에 의미가 있다. > 매개변수가 많아지면 호출시 인수의 순서를 고려 > 실수 유발 > 유지보수성 나빠짐
- 매개변수는 코드를 이해하는데 방해되는 요소 > 이상적인 매개변수 개수는 0개, 소소익선
- 이상적인 함수는 한 가지 일만 해야하며 가급적 작게 만들어야 한다.
- 매개변수는 최대 3개 이상을 넘지 않는 것 권장
- 만약 그 이상이 필요하다면 하나의 매개변수를 선언하고, 객체를 인수로 전달하자.
- 객체를 인수로 사용하면, 프로퍼티 키만 정확하면 매개변수의 순서는 신경 쓰지 않아도 된다.
- 또한 명시적으로 인수의 의미를 설명하는 프로퍼티키를 사용하게 되므로 가독성 up, 실수 down
- but, 함수 외부에서 내부로 전달한 객체를 함수 내부에서 변경하면 함수 외부의 객체가 변경된다(부수 효과 발생)
반환문
- return 키워드와 표현식(반환값)으로 이뤄진 반환문을 사용해 실행 결과를 함수 외부라 반환(return)할 수 있다.
- 함수 호출은 표현식 > 함수 호출 표현식은 return 키워드가 반환한 표현식의 평가 결과(반환값)으로 평가된다.
- 반환문은 두가지 역할을 한다.
- 반환문은 함수의 실행을 중단하고 함수 몸체를 빠져나간다. 반환문 이후에 존재하는 다른 문은 실행x
- 반환문은 return 키워드 뒤에 오는 표현식을 평가해 반환한다.
- return 키워드 뒤에 반환값으로 사용할 표현식을 명시적으로 지정 x > undefined 반환
- 반환문은 생략 가능한데 이때도 암묵적으로 undefined 반환
function add(a = 0, b = 0) {
return // ASI에 의해 세미콜론 추가
a + b; // 무시된다.
}
console.log(add(7, 5)); //undefined
- return 키워드와 반환값 표현식 사이에 줄바꿈이 있으면 세미콜론 자동 삽입 기능에 의해 세미콜론이 추가되어 undefined 발생
- 반환문은 함수 몸체 내부에서만 사용 가능. 전역에서 반환문 사용시 문법에러 발생
- Node는 모듈 시스템에 의해 파일별로 독립적인 파일 스코프를 갖음 > 파일의 가장 바깥 영역에 반환문을 사용해도 에러 발생 x
참조에 의한 전달과 외부 상태의 변경
- 매개변수도 함수 몸체 내부에서 변수와 동일하게 취급 > 값에 의한 전달, 참조에 의한 전달 그대로 동작
function changeVal(primitive, obj) {
primitive += 100;
obj.name = "jo";
}
var num = 100;
var person = { name: "Kim" };
console.log(num, person); //100 { name: 'Kim' }
changeVal(num, person);
console.log(num, person); //100 { name: 'Kim' }
// person은 원본이 훼손됨
- 원시 타입 인수를 전달받은 primitive는, 불변값이므로 재할당을 통해 할당된 원시값을 새로운 원시값으로 교체
- 객체 타입 인수를 전달받은 obj는, mutable 값이므로 재할당 없이 직접 할당된 객체를 변경
- 원시 타입 인수는 값 자체가 복사되어 매개변수에 전달 > 함수 몸체에서 그 값을 변경(재할당을 통한 교체)해도 원본은 훼손 x
- 함수 외부에서 함수 몸체 내부로 전달한 원시 값의 원븐올 변경하는 부수효과 x
- 객체 타입 인수는 참조 값이 복사되어 전달 > 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본 훼손
- 함수 외부에서 내부로 전달한 참조값에 의해 원본 객체가 변경되는 부수효과 o
- 함수 외부 상태를 변경하면 상태 변화 추적이 어려워진다. > 코드 복잡성 up, 가독성 down
- 객체의 변경을 추적하려면 옵저버 패턴 등을 통해 객체 참조를 공유하는 모든 이들에게 변경 사실을 통지하고 이에 대처하는 추가 대응이 필요
- 이러한 문제의 해결 방법 중 하나는 객체를 불변 객체로 만들어 사용하는 것.
- 객체의 복사본을 새롭게 생성하는 비용은 들지만 객체를 마지 원시값처럼 immutable한 값으로 동작하게 함
- 이를 통해 객체의 상태 변경 x 상태 변경이 필요한 경우 객체의 방어적 복사를 통해 원본 객체를 완전히 복제 (깊은 복사)해 새로운 객체를 생성, 재할당을 통해 교체 (부수효과 x)
- 순수 함수 : 외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수
- 순수 함수를 통해 부수효과를 최대한 억제, 오류 피하고 프로그램의 안정성을 높이려는 프로그래밍 패러다임 ⇒ 함수형 프로그래밍
다양한 함수의 형태
즉시 실행 함수
- 함수 정의와 동시에 즉시 호출되는 함수 (IIFE)
- 즉시실행함수는 단 한번만 호출되며 다시 호출할 수 없다.
(function () {
var a = 3;
var b = 5;
return a + b;
})();
- 즉시 실행 함수는 익명 함수를 사용하는 것이 일반적.
- 기명 즉시 실행 함수도 사용 가능. but 그룹 연산자 내의 기명 함수는, 함수 선언문이 아니라 함수 리터럴로 평가되어 함수 이름은 함수 몸체에서만 참조할 수 있는 식별자 > 다시 호출할 수 없다.
(function foo() { //함수 foo는 함수 리터럴로 평가
var a = 3;
var b = 5;
return a + b;
})();
console.log(foo()); //ReferenceError: foo is not defined
- 즉시 실행 함수는 반드시 그룹 연산자로 감싸야 한다.
- 그렇지 않으면 함수 선언문인데 형식에 맞지 않다고 평가하여 에러 발생
// 익명함수인데 () x
function(){
//...
}()
// SyntaxError: Function statements require a function name
// 함수 선언문인데 형식에 맞지 않아서 에러 발생
// 기명함수인데 () x
function foo() {}(); //SyntaxError: Unexpected token ')'
// {}; ()로 인식
// 함수 선언문 뒤에 ()를 함수 호출 연산자가 아닌 그룹 연산자로 해석
// 그룹 연산자의 피연산자가 없어서 에러 발생
(); //SyntaxError: Unexpected token ')'
- 그룹 연산자로 함수를 묶은 이유 : 함수 리터럴을 평가해서 함수 객체를 생성하기 위해.
- 따라서 먼저 함수 리터럴을 평가해서 함수 객체를 생성할 수 있다면 그룹 연산자 이외의 연산자를 사용해도 ok
(function(){
//,,,
}())
(function(){
//...
})()
!function(){
//...
}()
+function(){
//...
}()
- 즉시 실행 함수도 일반 함수처럼 값을 반환할 수 있고 인수를 전달할 수도 있다.
- 즉시 실행 함수내에 코드를 모아두면 혹시 있을 수 있는 변수나 함수 이름의 충돌을 방지
// 즉시실행함수도 일반 함수처럼 값 반환 가능
var res = (function () {
var a = 4;
var b = 5;
return a + b;
})();
console.log(res); //9
// 즉시실행함수도 일반 함수처럼 인자 전달 가능
var res = (function (a, b) {
return a + b;
})(3, 5);
console.log(res); //8
재귀 함수
- 재귀 호출 : 함수가 자기 자신을 호출하는 것
- 재귀 함수 : 재귀 호출을 수행하는 함수
- 반복되는 처리를 위해 사용
- 재귀 함수 내부에서 자기 자신을 호출할 때 식별한 호출자는 함수 이름이다.
- 함수 이름은 몸체 내부에서만 유효
- 따라서 함수 내부에서는 함수 이름을 사용해 자기 자신 호출 가능
- 함수 표현식으로 정의한 함수 내부에서는 함수 이름, 함수를 가리키는 식별자로도 재귀 호출 가능
- 함수 외부에서 함수를 호출할 때는 반드시 함수를 가리키는 식별자로 해야한다.
- 재귀 함수는 자신을 무한 재귀 호출 > 탈출 조건 반드시!
- 탈출 조건 없으면 스택 오버플로 에러 발생
- 재귀함수는 반복되는 처리를 반복문 없이 구할 수 있다는 장점이 있지만, 무한 반복에 빠질 위험 Up, 스택오버플로 에러 발생 up 주의!
- 반복문 사용했을 때보다 재귀를 사용하는게 더 직관적일 때만 사용
중첩 함수
- 함수 내부에 정의된 함수 : 중첩 함수 or 내부 함수
- 중첩 함수를 포함하는 함수 : 외부 함수
- 중첩 함수는 외부 함수 내부에서만 호출 가능
- 일반적으로 중첩함수는 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
- es6부터 함수 정의는 문이 위치할 수 있는 문맥이라면 어디든 가능
- es6이전 함수 선언문 : 코드이 최상위, 다른 함수 내부에서만 정의가능 but 지금은 if, for 등의 코드 블록 내에서도 정의 가능
- but 호이스팅으로 인한 혼란을 막기위해 코드 블록 내의 함수 선언문 정의는 지양
콜백 함수
- 함수를 합성하여, 함수의 변하지 않는 공통 로직은 미리 정의해두고, 경우에 따라 반복되는 로직은 추상화하여 함수 외부에서 내부로 전달한다.
function repeat(n, f) { //고차함수
for (var i = 0; i < n; i++) {
f(i); // 변경되는 로직을 f로 추상화, 이를 외부에서 전달받음
}
}
// repeat 함수는 더이상 내부 로직에 강력히 의존하지 않고, 외부에서 로직의 일부분을 함수로 전달 받아 수행
var logAll = function (i) { //콜백함수
console.log(i);
};
repeat(5, logAll); // 01234
// 콜백함수 호출이 아닌 자체를 전달
var logOdds = function (i) { //콜백함수
if (i % 2) console.log(i);
};
repeat(5, logOdds); //13
- 콜백 함수 : 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
- 고차 함수 : 매개변수를 통해 외부에서 콜백 함수를 전달받은 함수, 반환값으로 함수를 반환하는 함수
- 콜백 함수도 헬퍼 함수의 역할을 한다.
- 중첩 함수는 고정되어 있어 교체가 곤란, 콜백 함수는 자유롭게 교체 가능
- 고차함수는 콜백 함수를 자신의 일부분으로 합성한다.
- 고차함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출
- 콜백함수는 고차 함수에 의해 호출되며, 이때 고차함수는 필요에 따라 콜백함수에 인수를 전달할 수 있다.
따라서 고차함수에 콜백함수를 전달할 때 콜백 함수를 호출하지 않고 함수 자체를 전달해야 한다.
- 콜백 함수가 고차 함수 내부에만 호출된다면, 익명함수리터럴로 곧바로 고차함수에 전달하는 것이 일반적
repeat(5, function (i) { // 익명함수리터럴로 고차함수에 전달
if (i % 2) console.log(i);
});
// 익명함수리터럴은 repeat함수 호출할때 마다 평가되어 함수 객체 생성
// 고차 함수가 호출될때마다 콜백 함수 생성
// 콜백함수를 다른 곳에서도 호출할 필요가 있거나, 고차함수가 자주 호출된다면
// 함수 외부에서 콜백 함수를 정의한 후 함수 참조를 고차 함수에 전달하는 것이 효율족
var logOdds = function (i) {
if (i % 2) console.log(i);
};
// logOdds는 단 한번만 생성
repeat(5, logOdds);
- 콜백함수는 비동기 처리, 배열 고차 함수에서 사용
순수함수와 비순수함수
- 순수 함수 : 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 부수 효과가 없는 함수
- 비순수 함수 : 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수 효과가 있는 함수
- 순수 함수 : 동일한 인수가 전달되면 언제나 동일한 값 반환
- 어떤 외부상태에도 의존하지 않고, 오직 매개변수를 통해 함수 내부로 전달된 인수에게만 의존해 반환값을 생성
- 함수의 외부 상태에 의존하는 함수는 함수의 외부상태에 따라 반환값이 달라진다.
- 함수의 외부 상태를 변경하지 않는다.
- 순수 함수는 어떤 외부 상태에도 의존하지 않으며 외부 상태를 변경하지도 않는 함수
- 비순수 함수 : 함수의 외부 상태에 따라 반환값이 달라지는 함수, 외부 상태에 의존하는 함수
- 함수의 외부 상태를 변경하는 부수 효과 o
- 비순수함수는 외부 상태에 의존하거나 외부 상태를 변경하는 함수
var cnt = 0;
function increase(n) {
return ++n;
}
cnt = increase(cnt);
console.log(cnt); //1
cnt = increase(cnt);
console.log(cnt); //2
cnt = 0;
function increase2() {
return ++cnt;
}
// 함수 내부에서 함수 외부 상태를 직접 잠초
// 외부 상태에 의존하여 반환값이 변할 수 있다.
// 외부 상태를 변경할 수 있다.
// 함수 내부에서 외부 상태를 직접 참조하지 않더라도 매개변수를 통해 객체를 전달받으면,
// 비순수 함수가 된다.
// 상태 변화 추적 어렵다.
increase2();
console.log(cnt); //1
increase2();
console.log(cnt); //2
- 함수형 프로그래밍 : 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화 > 불변성 지향하는 프로그래밍 패러다임
- 로직 내에 존재하는 조건문과 반복문을 제거해서 복잡성 해결, 변수 사용 억제 + 생명주기 최소화하여 상태 변경을 피해 오류 최소화 하는 것이 목표
- 조건문, 반복문 : 로직 흐름 바꿔 가독성 해침
- 변수 : 변경 가능하여 오류 발생의 근본적 원인
- 함수형 프로그래밍은 결국 순수 함수를 통해 부수 효과를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높이려는 노력의 일환
- js는 멀티 패러다임 언어 ⇒ oop + 함수형 프로그래밍 적극 활용