함수의 매개변수도 타입에 따라 값에 의한 전달, 참조에 의한 전달 방식을 그대로 따른다.
function changeVal(primitive, obj){
primitive += 100;
obj.name = 'Kim';
}
let num = 100;
let person = { name : 'Lee' };
changeVal(num, person);
console.log(num); // 100
console.log(person); // {name:'Kim'}
위 코드를 보면 객체타입의 값이 변했다.
그 이유는 원시값은 재할당을 통해 새로운 원시값으로 교체했지만 객체 타입은 참조값을 할당하기 때문에 할당된 객체가 변한것이다.
이러한 문제의 해결 방법은 객체를 불변 객체로 만들어 사용하는 것이다.
외부상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수를 순수 함수라 한다.
📌 순수 함수를 통해 부수효과를 최대한 억제하여 오류를 피하고 프로그램의 안전성을 높이려는 프로그래밍 패러다임을 함수형 프로그래밍이라 한다.
함수 정의와 동시에 즉시 호출되는 함수
(function foo() { // 이름을 생략하면 익명 즉시실행 함수
let a = 3;
let b = 5;
return a * b;
}());
let res = (function (a, b) {
return a * b;
}(3, 5));
즉시 실행 함수는 함수 이름이 없는 익명 함수를 사용하는 것이 일반적이다.
즉시 실행 함수를 다시 호출할 수는 없다.
즉시 실행 함수는 반드시 그룹 연산자(...)로 감싸야 한다.
그룹 연산자로 함수를 묶은 이유는 함수 리터럴을 평가해서 함수 객체를 생성하기 위해서다.
function fac(n){
if (n <=1) return 1;
return n + fac(n - 1);
}
let factorial = function foo(n){
if (n <=1) return 1;
return n + factorial(n - 1);
// return n * foo(n - 1); 도 가능
}
첫번째 코드를 보면 다른 언어들과 유사하지만 두번째 코드는 자바스크립트 언어의 특성이 보인다.
자바스크립트는 함수 이름은 물론 함수를 가리키는 식별자로도 자기 자신을 재귀 호출할 수 있다. 단, 함수 외부에서 함수를 호출할 때는 함수를 가리키는 식별자로 해야한다.
📌 재귀 함수는 무한반복에 빠질수 있고 스택 오버플로 에러를 발생시킬 수 있으므로 주의해서 사용해야 한다.(탈출 조건 필수!)
함수 내부에 정의된 함수를 중첩 함수 또는 내부 함수라 한다.
중첩 함수는 함수 내부에서만 호출할 수 있다.
중첩 함수는 자신을 포함하는 외부 함수를 돕는 헬퍼 함수의 역할을 한다.
function outer() {
var x = 1;
function inner() {
var y = 2;
console.log(x + y);
}
inner();
}
outer();
ES6부터 함수 정의는 문이 위차할 수 있는 문백이라면 어디든지 가능하다. (if문, for문 등의 코드블록 내에서도 가능)
📌 호이스팅으로 인해 혼란이 발생할 수 있으므로 코드블록 내에서는 함수 선언문을 통해 함수를 정의 하는 것은 ❌
function repeat(n, f){
for (var i = 0; i < n; i++){
f(i);
}
}
var logAll = function (i){
console.log(i);
};
var logOdds = function (i) {
if (i % 2) console.log(i);
}
repeat(5, logAll);
repeat(5, logOdds);
위 코드는 공통 로직은 미리 정의해 두고, 경우에 따라 변경되는 로직은 추상화 해서 함수 외부에서 함수 내부로 전달한다.
이처럼 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수를 콜백 함수라 하며, 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수라고 한다.
고차 함수는 콜백 함수를 자신의 일부분으로 합성한다.
고차 함수는 매개변수를 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출한다. 콜백 함수는 고차 함수에 의해 호출되며 이떄 고차함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
repeat(5, funtion(i){
if (i % 2) console.log(i);
}):
콜백 함수가 고차 함수 내부에서만 호출된다면 익명 함수 리터럴로 고차함수에 전달하는 것이 일반적이지만, 콜백 함수가 자주 호출된다면 함수 외부에서 정의한 후 고차 함수에 전달하는 것이 효율적이다.
📌 콜백 함수는 비동기 처리뿐 아니라 배열 고차함수에서도 사용되기 때문에 중요한 패턴이다
함수형 프로그래밍에서는 어떤 외부 상태에 의존하지도 않고 변경하지도 않는, 즉 부수효과가 없는 함수를 순수함수라 하고, 외부 상태에 의존하거나 외부 상태를 변경하는, 즉 부수효과가 있는 함수를 비순수 함수 라고 한다.
var count = 0;
function increase(n){
return ++n;
}
순수 함수는 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수다.
var count = 0;
function increase(n){
return ++count;
}
위 코드처럼 함수 내부에서 외부 상태를 직접 참조하면 외부 상태에 의존하게 되어 반환값이 변할 수 있고, 외부 상태도 변할 수 있으므로 비순수 함수가 된다.
객체를 매개변수로 받게 되면 또한 비순수 함수가 된다.
📌 함수형 프로그래밍은 순수 함수를 통해 부수 효과를 최대한 억제해 오류를 피하고 프로그램의 안정성을 높이려는 일환이다. 자바스크림트는 멀티 패러다임 언어이므로 객체지향 프로그램뿐만 아니라 함수형 프로그래밍을 적극적으로 활용하고 있다.
리액트를 쓰면서 함수형만 써왔는데 왜 객체지향에서 함수형으로 바뀌었을까 생각해본적이 없었다.
더 많은 이유가 있겠지만 자바스크립트의 객체지향은 부수효과가 생길 위험이 크기 때문에 순수 함수를 사용한 함수형 프로그래밍을 사용한다고 생각이 들았다.
앞으로도 함수형을 쓸것 같긴한데 최대한 순수한 함수로 부수효과들을 생각하면서 코드를 짜야겠다.