- 클로저(closure)
정의 : "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다.
→ 클로져 함수는 "외부함수의 컨텍스트(외부함수의 변수등)에 접근할 수 있는 내부함수"
→ 클로저는 반환된 내부함수가 자신이 선언됐을 때의 환경(Lexical environment)인 스코프를 기억하여 자신이 선언됐을 때의 환경(스코프) 밖에서 호출되어도 그 환경(스코프)에 접근할 수 있는 함수 / 클로저는 자신이 생성될 때의 환경(Lexical environment)을 기억하는 함수
→ 스코프는 함수를 호출할 때가 아니라 함수를 어디에 선언하였는지에 따라 결정된다. 이를 렉시컬 스코핑(Lexical scoping)라 한다.
----------------------------------------------------------------------------------------------------------------
→ 클로저 함수는 "함수를 리턴하는 함수" 라는 점, 함수를 리턴하는 함수가 클로저의 형태를 만듭니다. / 클로저와 클로저 함수는 서로 같지 않다.
→ 어휘적 환경(lexical environment) : 변수 및 함수 선언의 형태
→ 클로저는 리턴하는 함수에 의해 스코프(변수의 접근 범위)가 구분
→ 클로저의 핵심은 스코프를 이용해서, 변수의 접근 범위를 닫는(closure; 폐쇄) 데에 있습니다
→ 함수를 리턴하는 것만큼이나, 변수가 선언된 곳이 중요
→ x가 선언된 함수는 바깥쪽에 있으니 '외부 함수' / x는 외부함수에서 선언된 변수
→ y가 선언된 함수는 보다 안쪽에 있으니 '내부 함수' / y는 내부함수에서 선언된 변수
/ return된 함수를 내부 scope로 지정한다
→ 함수의 scope에 따라 접근할 수 있는 변수의 차이가 있음
→ 클로저 함수는 "내부 함수는 외부 함수에 선언된 변수에 접근 가능하다"는 점
[클로저 함수의 특징]
→ 위에 경우 변수 add5 에는 클로저를 통해 리턴한 함수가 담겨 있습니다.
/ add5는 function (y) { return x+y;} 라는 함수를 담고 있다. 하지만 외부 함수에서 사용한 x라는 변수를 사용할수 있다.
/ 외부함수의 실행이 끝나도 외부함수에서 선언된 변수를 저장하고 있음(클로저 함수의 특징)
→ 일반적인 함수는, 함수 실행이 끝나고 나면 함수 내부의 변수를 사용할 수 없지만 클로저는 외부 함수의 실행이 끝나더라도, 외부 함수 내 변수가 메모리 상에 저장되어 외부 함수의 변수를 사용할 수 있습니다.
/ 어휘적 환경을 메모리에 저장하기 때문에 가능한 일입니다
→ add5함수는 function(y) {return x+y;}이지만 외부 함수에서 사용한 x를 사용할 수 있기 때문에 매개변수로 y값을 할당해 준다면 정상적으로 결과값을 return한다.
→ 화살표 함수에서 앞에 쓰인 변수가 외부함수에서 선언된 외부함수 scope내 선언된 변수이며
뒤어 쓰인 변수가 내부함수에서 선언된 내부함수 scope내 선언된 변수입니다.
→ `divMaker` 함수는 'div'라는 문자열을 `tag` 라는 변수에 담아두고 있으며, `anchorMaker` 함수는 'a'라는 문자열을 `tag`에 담아두고 있습니다.
→ 클로저는 이처럼 특정 데이터를 스코프 안에 가두어 둔 채로 계속 사용할 수 있게 해줍니다.
→ const tagMaker = tag => content => `<${tag}>${content}</${tag}>`
함수표현식으로 변경한 경우 :
/ const tagMaker = function (tag) {
return function(content) { return `<${tag}>${content}</${tag}>`; }
}
→ 클로저를 이용해 내부 함수를 단 하나만 리턴하는 것에 그치지 않고, 객체에 담아 여러 개의 내부 함수를 리턴하도록 만듭니다.
→ makeCounter 함수는 increase, decrease, getValue메소드를 포함한 객체 하나를 리턴함
/ counter1은 객체입니다.
→ 함수표현식으로 변경 :
const makeCounter = function() {
let value = 0; // 외부 함수내 선언된 변수이므로 makeCounter함수가 실행되고 객체를 반환한 경우 closure가 아닌경우에는 사용할 수없지만 closure는 외부 함수의 변수도 사용할 수 있기 때문에 해당 변수를 사용한다.
return {
increase : function(){return value = value + 1;}
, decrease : function(){return value = value -1;}
, getValue : function(){return value;}
}
}
→ '외부 스코프에서는 내부 스코프의 변수에 접근할 수 없다'는 규칙에 의해, 어떤 경우에도 `value`는 직접 수정이 불가능
→ 대신, 리턴하는 객체가 제공하는 메소드를 통해 `value` 값을 간접적으로 조작할 수 있습니다. / 객체명.getValue()을 이용하여 접근
→ 이것이 바로 정보의 접근 제한 (캡슐화) 입니다.
→ 만일 스코프로 value 값을 감싸지 않았더라면, value 값은 전역 변수여야만 했을 것입니다. 하지만 makeCounter라는 함수가 value 값을 보존하고 있기 때문에, 전역 변수로 따로 만들 필요가 없습니다.
→ 전역 변수가 좋지 않은 이유는, 전역 변수는 다른 함수 혹은 로직 등에 의해 의도되지 않은 변경을 초래하는데(이를 side effect라 한다)side effect를 최소화하면, 의도되지 않은 변경을 줄일 수 있어 오류로부터 보다 안전하게 값을 보호할 수 있다.
→ 클로저를 통해 불필요한 전역 변수 사용을 줄이고, 스코프를 이용해 값을 보다 안전하게 다룰 수 있습니다. / 캡슐화의 사용 목적
→ 객체에서 메소드(속성값이 함수인 경우)를 접근하기 위해서는 ‘객체명.key값()’으로 접근
→ makeCounter에 의해 리턴된 객체는, makeCounter를 실행할 때에 선언되는 value 값을 각자 독립적으로 가지게 됩니다
→ 따라서 counter1에서의 value와 counter2에서의 value는 서로에게 영향을 끼치지 않고, 각각의 값을 보존할 수 있습니다.
→ 전역변수가 아닌 클로저함수의 변수이므로 각자 독립적으로 사용된다.
[모듈화]
→ 함수 재사용성을 극대화하여, 함수 하나를 완전히 독립적인 부품 형태로 분리하는 것을 모듈화라고 합니다.
→ 클로저를 통해 데이터와 메소드를 같이 묶어서 다룰 수 있기때문에 클로저는 모듈화에 유리합니다.
→ 클로저에 의해 참조되는 외부함수의 변수를 자유변수(Free variable)라고 부른다
[클로저가 아닌 경우 ]
let multiplyByFive = function() {
return function(y) {
return 5 * y;
}
}
let multiplyBy5
multiplyBy5 = multiplyByFive();
multiplyBy5(4);
→ 외부함수의 컨텍스트에 접근하고 있지 않기 때문에 익명함수를 반환하더라도 클로저는 아님
→함수 내에서 다른 함수(내부 함수)가 리턴이 되면, 이 함수를 클로져 함수라고 부르고, 외부 함수에 있는 변수에 접근 가능하다
- 클로저 추가사항
→ 클로져는 "함수와 함수가 선언된 어휘적(lexical) 환경의 조합을 말한다. 이 환경은 클로저가 생성 된 시점의 유효 범위 내에 있는 모든 지역 변수로 구성된다." 라고 합니다. 여기서의 키워드는 "함수가 선언"된 "어휘적(lexical) 환경"입니다. 특이하게도 자바스크립트는 함수가 호출되는 환경와 별개로, 기존에 선언되어 있던 환경 - 어휘적 환경 - 을 기준으로 변수를 조회하려고 합니다. 유어클레스 영상에서 언급되는 "외부함수의 변수에 접근할 수 있는 내부함수"를 클로져 함수로 부르는 이유도 그렇습니다
→ 클로저 함수: 클로저는 외부함수의 컨텍스트에 접근할 수 있는 내부함수를 뜻합니다. 외부함수의 실행이 종료된 후에도, 클로저 함수는 외부함수의 스코프, 즉, 함수가 선언된 어휘적 환경에 접근할 수 있습니다.
→ 클로저 사용 예시: 클로저를 통해 커링(currying, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어 각각 인자를 받게 하는 방법), 클로저 모듈(변수를 외부 함수 스코프 안쪽에 감추어, 변수가 함수 밖에서 노출되는 것을 막는 방법) 등의 패턴을 구현할 수 있습니다.
→ 클로저의 단점: 일반 함수였다면 함수 실행 종료 후 가비지 컬렉션(참고 자료: MDN '자바스크립트의 메모리 관리') 대상이 되었을 객체가, 클로저 패턴에서는 메모리 상에 남아 있게 됩니다. 외부 함수 스코프가 내부함수에 의해 언제든지 참조될 수 있기 때문입니다. 따라서 클로저를 남발할 경우 퍼포먼스 저하가 발생할 수도 있습니다. 자바스크립트는 가비지 컬렉션을 통해 메모리 관리를 합니다. 객체가 참조 대상이 아닐 때, 가비지 컬렉션에 의해 자동으로 메모리 할당이 해제됩니다.
'자바스크립트' 카테고리의 다른 글
DOM에 대한 전반적인 개념 (0) | 2021.05.07 |
---|---|
Spread문법과 Rset (0) | 2021.05.07 |
스코프(scope)의 전반적인 개념 (0) | 2021.05.07 |
원시자료형 & 참조자료형 (0) | 2021.05.07 |
정규표현식 (0) | 2021.05.07 |