12.5 참조에 의한 전달과 외부 상태 변경
- changeVal 함수는 매개변수를 통해 전달받은 원시 타입 인수와 객체 타입 인수를 함수 몸체에서 변경
//매개 변수 primitive는 원시 값을 전달받고, 매개변수 obj는 객체를 전달받음
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"}
원시 값 primitive
- 원시 값은 변경 불가능한 값
- 재할당을 통해 할당된 원시 값을 새로운 원시 값으로 교체
- 함수 외부에서 함수 몸체 내부로 전달한 원시 값의 원본을 변경하는 어떠한 부수 효과도 발생하지 않음
객체 값 person
- 참조 값이 복사되어 매개변수에 전달되기 때문에 함수 몸체에서 참조 값을 통해 객체를 변경할 경우 원본이 훼손 됨
- 함수 외부에서 함수 몸체 내부로 전달한 값에 의해 원본 객체가 변경되는 부수효과 발생
해결방법
1) 옵저버 패턴
- 객체의 변경을 추적
- 객체의 참조를 공유하는 모든 이들에게 변경 사실을 통지하고 이에 대처하는 추가 대응 필요
2) 불변 객체 (immutable object)
- 객체의 상태 변경을 원천 봉쇄
- 객체의 상태 변경이 필요한 경우 객체의 방어적 복사(defensive copy)를 통해 새로운 객체를 생성하고 재할당을 통해 교체
12.6 다양한 함수의 형태
12.6.1 즉시 실행 함수
- 함수 정의와 동시에 즉시 호출되는 함수
- 단 한번만 호출되며 다시 호출 불가
//익명 즉시 실행 함수
(function () {
var a=3;
var b=5;
return a*b;
}());
//기명 즉시 실행 함수
(function foo () {
var a=3;
var b=5;
return a*b;
}());
- 즉시 실행 함수는 반드시 그룹 연산자 (..)로 감싸야 함
- 즉시 실행 함수 내에 코드를 모아 두면 혹시 있을 수도 있는 변수나 함수 이름의 충돌을 방지할 수 있음
12.6.2 재귀 함수
재귀 호출(recursive call)
- 함수가 자기 자신을 호출하는 것
재귀 함수(recursive function)
- 재귀호출을 수행하는 함수
- 반복되는 처리를 위해 사용
ex) 팩토리얼
//팩토리얼은 1부터 자신까지의 모든 양의 정수의 곱
function factorial(n) {
//탈출 조건 : n이 1이하일 떄 재귀 호출을 멈춤
if(n<=1) return 1;
//재귀 호출
return n*factorial(n-1);
}
console.log(factorial(5)); //5!=5*4*3*2*1=120
- 함수 이름과 식별자를 통해 재귀 호출 가능
- 함수 외부에서 함수 호출을 할때는 반드시 함수를 가리키는 식별자로 해야함
//함수 표현식
var factorial = function foo(n) {
//탈출 조건 : n이 1 이하일 때 재귀 호출을 멈춤
if(n<=1) return 1;
//함수를 가리키는 식별자로 자기 자신을 재귀 호출
return n*factorial(n-1);
//함수 이름으로 자기 자신을 재귀 호출 가능
//console.log(facorial === foo ); //true
//return n * foo(n-1);
};
console.log(factorial(5)); //5! = 5*4*3*2*1 = 120
12.6.3 중첩 함수
중첩 함수
- 함수 내부에 정의된 함수
- 자신을 포함하는 외부 함수를 돕는 헬퍼함수의 역할
외부 함수
- 중첩함수를 포함하는 함수
- 중첩 함수는 외부 함수 내부에서만 호출 가능
12.6.4 콜백 함수
- 반복하는 일은 변하지 않고 공통적으로 수행하지만 반복하면서 하는 일의 내용은 다름
- 함수의 일부분만이 다르기 때문에 매번 함수를 새롭게 정의해야함
- 내부 로직에 강력히 의존
//n만큼 어떤 일을 반복한다.
function repeat1(n) {
//i를 출력한다.
for(var i = 0; i < n; i++) console.log(i);
}
repeat1(5);
//n만큼 어떤일을 반복한다.
function repeat2(n) {
for(var i =0; i<n; i++) {
//i가 홀수 일때 출력
if(i%2) console.log(i);
}
}
repeat2(5);
- 함수의 변하지 않는 공통 로직은 미리 정의해 두고, 경우에 따라 변경되는 로직은 추상화 해서 함수 외부에서 함수 내부로 전달
//외부에서 전달받은 f를 n만큼 반복 호출
function repeat(n,f) {
for(var i = 0; i < n; i++) {
f(i); //i를 전달하면서 호출
}
}
var logAll = function(i) {
console.log(i);
};
//반복 호출할 함수를 인수로 전달
repeat(5,logAll); //0 1 2 3 4
var logOdds = function (i) {
if(i % 2) console.log(i);
}
//반복 호출할 함수를 인수로 전달
repeat(5,logOdds); //1 3
- repeat 함수는 경우에 따라 변경되는 일을 함수 f로 추상화하고 , 이를 외부에서 전달받음
콜백 함수 (callback function)
- 함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수
- 함수형 패러다임, 비동기 처리에 활용되는 중요한 패턴
고차 함수 (Higher-Order function)
- 매개변수를 통해 함수의 외부에서 콜백함수로 전달 받은 함수
- 고차함수는 콜백함수를 자신의 일부분으로 합성
- 고차함수는 매개변수로 통해 전달받은 콜백 함수의 호출 시점을 결정해서 호출
12.6.5 순수 함수와 비순수 함수
순수 함수 (pure function)
- 어떤 외부 상태에도 의존하지 않고 변경하지도 않는 함수
- 동일한 인수가 전달되면 언제나 동일한 값을 반환하는 함수
- 함수의 외부상태를 변경하지 않음
var count = 0; //현재 카운트를 나타내는 상태
//순수 함수 increase는 동일한 인수가 전달되면 언제나 동일한 값 반환
function increase(n) {
return ++n;
}
//순수 함수가 반환한 결과값을 변수에 재할당해서 상태를 변경
count = increase(count);
console.log(count); //1
count = increase(count);
console.log(count); //2
비순수 함수 (impure function)
- 외부상태에 의존하거나 외부 상태를 변경하는 함수
- 함수 내부에서 외부 상태를 직접 참조
- 매개변수를 통해 객체를 전달 받음
var count = 0; //현재 카운트를 나타내는 상태 : increase 함수에 의해 변화
//비순수 함수
function increase() {
return ++count; //외부 상태에 의존하며 외부 상태를 변경
}
//비순수 함수는 외부 상태 (count)를 변경하므로 상태 변화를 추적하기 어려워짐
increase();
console.log(count); //1
increase();
console.log(count); //2
함수형 프로그래밍
- 순수 함수와 보조 함수의 조합을 통해 외부 상태를 변경하는 부수 효과를 최소화해서 불변성(immutability)을 지향하는 프로그래밍 패러다임
'JavaScript' 카테고리의 다른 글
[Deep dive] 14장 전역 변수의 문제점 (0) | 2023.08.09 |
---|---|
[Deep dive] 13장 스코프 (0) | 2023.08.09 |
[Deep dive] 12장 함수(1) (0) | 2023.08.08 |
[Deep dive] 11장 원시 값과 객체의 비교 (0) | 2023.08.08 |
[Deep dive] 10장 객체 리터럴 (0) | 2023.08.08 |