데이터가 한 번 생성되면 그 이후에는 변경할 수 없는 성질
===
)하는 것만으로 변화를 감지하고 화면을 효율적으로 업데이트할 수 있음// 나쁜 예: 원본 배열을 직접 변경 (Mutation)
const numbers = [1, 2, 3];
const addNumber_bad = (arr, num) => {
arr.push(num); // 원본 배열(numbers)을 직접 수정함
return arr;
};
const newNumbers_bad = addNumber_bad(numbers, 4);
console.log(newNumbers_bad); // [1, 2, 3, 4]
console.log(numbers); // [1, 2, 3, 4] <- 원본이 오염됨!
// 좋은 예: 새로운 배열을 반환 (Immutability)
const originalNumbers = [1, 2, 3];
const addNumber_good = (arr, num) => {
// 스프레드 문법(...)을 사용해 원본의 복사본을 만들고, 새 요소를 추가
return [...arr, num];
};
const newNumbers_good = addNumber_good(originalNumbers, 4);
console.log(newNumbers_good); // [1, 2, 3, 4]
console.log(originalNumbers); // [1, 2, 3] <- 원본이 그대로 보존됨!
// 나쁜 예: 부작용이 있는 함수 (Impure Function)
let taxRate = 0.1;
const calculatePrice_bad = (price) => {
// 함수 외부의 변수(taxRate)에 의존하고, 이를 직접 수정함
taxRate = 0.2; // 부작용(Side Effect) 발생!
return price * (1 + taxRate);
};
// 좋은 예: 순수 함수 (Pure Function)
const calculatePrice_good = (price, tax) => {
// 필요한 모든 값은 인자로 전달받고, 외부 상태를 변경하지 않음
return price * (1 + tax);
};
console.log(calculatePrice_good(1000, 0.1)); // 1100
// 몇 번을 호출하든, 입력이 같으면 결과도 항상 같다.
어떤 함수 호출을 그 함수의 결과값으로 대체해도 프로그램의 동작에 아무런 영향을 주지 않는 성질
순수 함수를 사용할 때 자연스럽게 따라오는 성질
const add = (a, b) => a + b; // 순수 함수
// 아래 두 줄의 코드는 완전히 동일하게 동작합니다.
const result1 = add(2, 3) + 5; // add(2, 3)이라는 표현식
const result2 = 5 + 5; // add(2, 3)을 그 결과값인 5로 대체
console.log(result1 === result2); // true
// add(2, 3)은 참조 투명성을 가집니다.
코드 블록과 그 블록이 정의된 문맥의 상수나 변수에 대한 참조를 함께 묶은 것
- 즉, 함수가 자기 자신이 생성된 환경(스코프)를 "기억"하고, 그 환경에 있던 변수들에 계속해서 접근할 수 있는 능력
- 외부 함수의 실행이 끝나서 그 생명주기가 다했더라도, 내부 함수는 여전히 외부 함수의 변수에 접근할 수 있다
- 함수와 다르게 이름이 필수가 아니며, 코드 내에서 직접 전달하거나 변수/상수에 할당할 수 있어 일급 객체(First-class citizen)로 취급된다.
// makeCounter 함수는 counter 함수를 반환하고 생을 마감한다.
const makeCounter = () => {
let privateCount = 0; // '비공개' 변수
// 반환되는 이 내부 함수가 바로 클로저다.
// 이 함수는 자신이 생성될 때의 환경(privateCount)을 기억한다.
return () => {
privateCount += 1;
console.log(privateCount);
};
};
const counter = makeCounter(); // counter 변수는 클로저 함수를 가리킴
counter(); // 1
counter(); // 2
counter(); // 3
// 외부에서는 privateCount에 직접 접근할 수 없다.
// console.log(privateCount); // ReferenceError: privateCount is not defined
구분 | 함수 | 클로저 |
---|---|---|
개념 | 코드 블록 | 자신이 선언된 환경을 기억하는 함수 |
변수 생명 주기 | 함수 실행 시 생성, 종료 시 소멸 | 외부 함수의 변수라도, 클로저가 참조하면 계속 유지됨 |
상태 유지 | 호출 간 상태를 유지하지 않음(Stateless) | 상태를 유지할 수 있음 (Stateful) |
주요 용도 | 특정 기능의 재사용 | 데이터 은닉(private 변수 흉내), 상태 유지, 함수 조합 |
예를 들어서, x+y 동작을 한다면 기본적으로는 이렇게 선언한다.
const adder(x) => {
// x는 adder 함수의 외부 변수
return (y) => {
// 반환되는 이 함수가 바로 클로저
return x + y;
};
};
const add5 = adder(5); // x가 5로 고정된 클로저
console.log(add5(10)); // 15 (5 + 10)
화살표 함수를 사용해 간결하게 만들 수 있다.
const adder = (x) => (y) => x + y; // 축약
const add5 = adder(5);
console.log(add5(10)); // 15 (5 + 10)