클로저는 함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다.
MDN - 클로저
조금 다르게 설명하면 외부 함수의 변수에 접근할 수 있는 내부 함수
라고 할 수도 있습니다.
let sum = function(x) {
let add = function(y) {
return x + y;
}
return add;
}
let result = sum(10);
console.log(result(3)); // 13
console.log(result(5)); // 15
위 예제처럼 클로저는 내부 함수인 add에서 y
변수 뿐만 아니라 외부 함수의 파라미터인 x
변수에 접근하여 사용이 가능합니다. 이러한 것이 가능하는 이유는 자바스크립트는 렉시컬 스코프
를 따르는 언어이기 때문이빈다.
어디서 호출했는지가 아닌 함수를 어디서 정의했는지에 따라 상위 스코프가 정해지는 것을 렉시컬 스코프(또는 정적 스코프)라고 말하다
출처: 모던 자바스크립트 Deep Dive
const x = 20;
function sum() {
const x = 100;
add();
}
function add() {
console.log(x + 10);
}
sum(); //30
add(); //30
렉시컬 스코프를 따르기 떄문에 정의 시점으로 살펴보았을때 sum()
과 add()
상위 스코프는 적역이 된다. 그렇기 때문에 sum()
내부 변수인 x
가 add()
의 x
에 영향을 주지 못한다.
이를 가능하기 위해서 자바스크립트에서는 내부 슬롯인 [[Environment]]
에 자신의 상위 스코프의 참조를 저장합니다.
클로저를 사용하는 것처럼 보이지만, 클로저의 특징을 활용하지 못하는 케이스가 있습니다.
function sum() {
const x = 100;
function add() {
const y = 10;
console.log(y + 10);
}
return add;
}
const add = sum();
add();
위의 경우 클로저라고 생각할 수도 있지만, 대부분의 브라우저에서 최적화를 하는 과정에서 상의 스코프의 어떠한 식별자도 참조하지 않는 경우 상위 스코프의 참조를 저장하지 않는다. 그렇기 때문에 클로저라고 부를 수 없다.
function sum() {
const x = 100;
function add() {
console.log(x + 10);
}
add();
}
const add = sum();
클로저의 경우 일반적으로 외부 함수(상위 함수)보다 내부 함수의 생명주기가 긴게 일반적입니다. 하지만 위의 경우 add 함수가 sum이 종료시점에 이미 반환되어 수행종료되었기 때문에 생명주기 더 짧기 때문에 클로저에 부합하지 않습니다.
커링
은 여러 인수 를 취하는 함수를 각각 단일 인수를 취하는 일련의 함수로 변환 하는 기술을 말한다.
출처 : 위키피디아 - 커링
function add(x) {
return function(y) {
return x + y;
}
}
let add3 = add(3);
console.log(add3(5)); //3 + 5 => 8
console.log(add3(2)); //3 + 2 => 5
커링을 사용하면 특정인자를 고정하여 재사용할 수 있다는 장점이 있습니다.
자바스크립트에서 모듈패턴은 클로저 기반으로 즉시 실행 함수로 함수와 변수를 감싸 만든 것을 말합니다. 이를 통해 클래스와 비슷한 구조를 가지게 됩니다.
(참고로 자바스크립트에 클래스가 존재하지만, 추후에 공부 후 다루도록 하겠습니다...)
var counter = (function () {
var privateCounter = 0;
return {
increment: function () {
++privateCounter;
},
decrement: function () {
--privateCounter;
},
getCounter: function () {
return privateCounter;
},
};
})();
자바스크립트에는 다른 언어와 다르게 접근 제한 키워드인 public, private, protected 등이 존재 하지 않는다. 하지만 클로저를 통한 모듈 패턴을 적용하면 접근 제한자를 사용할때와 비슷한 효과를 볼 수 있어 캡슐화
와 은닉화
가 가능해진다.
클로저는 상위 스코프 함수를 내부 함수가 언제든 접근 할 수 있도록 메모리에 남아 있습니다. 그렇기 때문에 클로저를 남발할 경우 성능 저하가 발생할 수도 있습니다.
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
const mergeArr = [...arr, ...arr2];
console.log(mergeArr); //[1, 2, 3, 4, 5, 6]
const obj = { x: 10, y: 20 };
const obj2 = { z: 30, d: 40 };
const mergeObj = {...obj, ...obj2};
console.log(mergeObj); //{ x: 10, y: 20, z: 30, d: 40 }
const arr = [1, 2, 3];
const copyArr = [...arr];
const obj = [1, 2, 3];
const copyObj = {...obj};
function func(a, ...args) {
console.log(args);
}
func("1", "2", "3", "4", "5");
//["2", "3", "4", "5"]
구조 분해 할당
은 배열 또는 객체를 해체후 새로운 변수에 할당 과정을 말합니다.
const [a, b, ...rest] = [1, 2, 3, 4];
console.log(a); // 1
console.log(b); // 2
console.log(rest); //[3, 4]
const {name, age, ...restObj} = {
name: 'jungsu',
age: 31,
phone: '010-1111-111',
company: 'macom',
};
console.log(name); // jungsu
console.log(age); // 31
console.log(restObj); //{phone: '010-1111-111', company: 'macom'}