
poiemaweb 배열 고차 함수 참고
poiemaweb 일급 객체 참고
고차 함수는 외부 상태 변경이나 가변(mutable) 데이터를 피하고 불변성(Immutability)을 지향하는 함수형 프로그래밍에 기반을 두고 있다.
함수형 프로그래밍은 순수 함수(Pure functino, 외부 상태를 변경하지 않고 외부 상태에 의존하지도 않는 함수)와 보조 함수의 조합을 통해 로직 내에 존재하는 조건문과 반복문을 제거하여 복잡성을 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임이다.
조건문, 반복문은 로직의 흐름을 이해하기 어렵게하여 가독성을 해치고, 변수의 값은 누군가에 의해 언제든지 변경될 수 있어 오류 발생의 근본적 원인이 될 수 있기 때문이다.
함수형 프로그래밍은 순수 함수를 통해 부수 효과(Side effect)를 최대한 억제하여 오류를 피하고, 프로그램의 안정성을 높이려는 노력의 한 방법이라고 할 수 있다.
자바스크립트는 고차 함수를 다수 지원하고 있다. 특히 Array 객체에는 매우 유용한 고차 함수를 제공한다.
✅ 원본 배열을 변경하는 메소드
✅ 원본 배열을 변경하지 않는 메소드
컴퓨터 프로그래밍 언어 디자인에서 일급 객체란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다.
보통 함수에 인자로 넘기기, 수정하기, 변수에 대입하기와 같은 연산을 지원할 때 일급 객체라고 한다. _위키백과 일급객체 참고
자바스크립트의 함수는 일급 객체이므로 아래와 같은 특징이 있다.
클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.
const x = 1;
function outerFunc() {
const x = 10;
function innerFunc() {
console.log(x);
}
innerFunc();
}
outerFunc();
outerFunc 함수 내부에서 중첩 함수 innerFunc가 정의되고 호출되었다.
이때 중첩 함수 innerFunc의 상위 스코프는 외부 함수 outerFunc의 스코프다.
따라서 중첩 함수 innerFunc 내부에서 자신을 포함하고 있는 외부 함수 outerFunc의 x변수에 접근할 수 있다.
만약 innerFunc 함수가 outerFunc 함수의 내부에서 정의된 중첩 함수가 아니라면 innerFunc 함수를 outerFunc 함수의 내부에서 호출한다 하더라도 outerFunc 함수의 변수에 접근할 수 없다.
이 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.
자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다.
이를 렉시컬 스코프(정적 스코프)라 한다.
스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다. 이 렉시컬 환경은 자신의 "외부 렉시컬 환경에 대한 참조"를 통해 상위 렉시컬 환경과 연결된다. 이것이 바로 스코프 체인이다.
렉시컬 환경의 "외부 렉시컬 환경에 대한 참조"에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다.
클로저는 상태(state)를 안전하게 변경하고 유지하기 위해 사용한다. 상태가 의도치않게 변경되지 않도록 상태를 안전하게 은닉(information hiding)하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.
function counter() {
let count = 0; // private 변수처럼 동작
return {
increase() { return ++count; },
decrease() { return --count; },
getCount() { return count; }
};
}
const count1 = counter();
console.log(count1.increase()); // 1
console.log(count1.increase()); // 2
console.log(count1.decrease()); // 1
function createUser(name) {
let greeting = `Hello, ${name}!`;
return {
sayHi() { console.log(greeting); },
changeName(newName) {
greeting = `Hello, ${newName}!`;
}
};
}
const user = createUser('Hoho');
user.sayHi(); // "Hello, Hoho!"
user.changeName('Koko');
user.sayHi(); // "Hello, Koko!"
const calculator = (function () {
let result = 0;
return {
add(n) {
result += n;
return this;
},
subtract(n) {
result -= n;
return this;
},
getResult() {
return result;
}
};
})();
calculator.add(5).subtract(2);
console.log(calculator.getResult()); // 3
function handleClick (message) {
return function (event) {
console.log(message, event.target);
}
}
button.addEventListener('click', handleClick('Button clicked: '));
function multiply (num1) {
return function (num2) {
return num1 * num2;
}
}
const multiplyByTwo = multiply(2);
console.log(multiplyByTow(4)); // 8
function fetchData (baseUrl) {
return function (endpoint) {
return function (callback) {
fetch(`${baseUrl}${endpoint}`)
.then(callback);
};
};
}
const getFromAPI = fetchData('https://api.example.com');
const getUsers = getFromAPI('/users');
getUsers((data) => console.log(data));
디바운스: 자주 발생하는 이벤트를 제어하여 성능을 최적화하는 기법
ex) 검색 시 API 호출을 줄이기위해 디바운스를 사용할 수 있음
// 디바운스 구현
function debounce(func, wait) {
let timeout;
return function executedFunction (...ars) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
}
}
// 사용 예
const handleSearch = debounce((event) => {
// API 호출 등의 무거운 작업
console.log('Searching: ', event.target.value);
}, 500);
searchInput.addEventListener('input', handleSearch);
클로저를 사용하여 이벤트 리스너를 설정하고, 해당 리스너를 제거하는 코드를 작성하여 메모리 해제에 신경써야 한다.
function heavyOperation () {
const bigData = new Array(1000000);
return function () {
return bigData;
}
}
// 메모리 누수 가능성
// bigData가 메모리에 계속 유지됨
const getData = heavyOperation();
functino setupHandler() {
const data = heavyData();
element.addEventListener('click', function () {
// data를 사용
});
// 클린업 함수 제공
return function cleanup() {
element.removeEventListener('click');
// data 참조 제거
data = null;
}
}