유인동님의 함수형 자바스크립트 프로그래밍
을 시작하며
자바스크립트의 함수와 관련된 몇 가지 기능들에 대해 알아보고자 합니다.
클로져
클로져는 자신이 생성될 때의 환경을 기억하는 함수이다. 이 문장만 보면 클로져가 너무 어렵다고 생각될 수가 있습니다. 따라서 조금 더 쉽게 표현하고자 예시를 들어보려 합니다.
예시
xfunction outerFunc() {
const outerVar = `I'm outer variable`;
function innerFunc() {
console.log(outerVar);
}
return innerFunc;
}
outerFunc()(); // I'm outer variable
클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수를 기억하는 함수다.
그렇다면, 자바스크립트의 모든 함수는 클로져냐는 질문이 가능합니다. 이는 해석하는 관점에 따라 다르지만 클로져의 조건에 하나를 더 달아 사용하도록 하겠습니다.
상단의 예시를 통해 보면 innerFunc
내에서 사용하는 변수 중 innerFunc
내에서 선언되지 않은 변수(outerVar
)가 있어야 합니다.
이러한 특성을가지고 클로져는 두 가지 이유에서 보통 사용됩니다.
- Side Effects를 관리하기위해
- Create Private Variables
Controlling side effects with closures
Ajax, Timeout등을 사용할 때, console.log
를 사용하는 것도 Side Effect를 야기합니다. 이와 같은 것들은 코드의 흐름을 방해할 수 있는데 이를 Closure를 통해 제어할 수 있습니다. 예시를 통해 알아보도록 합시다.
예시
xxxxxxxxxx
function makeCake() {
setTimeout(_ => console.log(`Made a cake`), 1000);
}
보시는 것과 같이 makeCake
함수는 Side Effect를 갖습니다. setTimeout
을 사용했기 때문입니다. 이제 makeCake
함수에 flavor
을 추가해보도록 하겠습니다.
xxxxxxxxxx
function makeCake(flavor) {
setTimeout(_ => console.log(`Made a ${flavor} cake!`), 1000);
}
makeCake
를 실행해보도록 하겠습니다.
xxxxxxxxxx
makeCake('banana') // Made a banana cake!
실행하였을 때, 1초가 지난 즉시 Made a banana cake!
라고 출력됩니다.
우리는 flavor
을 알자마자 Made a banana cake!
를 출력하는 것이 아닌, flavor
을 알게된 후에, 원하는 시점에 Made a banana cake!
를 출력하고 싶다 생각하고 새로운 함수 prepareCake
를 추가합니다.
prepareCake
함수 내부에서는 클로져인 makeCake
를 반환합니다.
xxxxxxxxxx
function prepareCake(flavor) {
return function () {
setTimeout(_ => console.log(`Made a ${flavor} cake!`), 1000);
};
}
const makeCakeLater = prepareCake('banana');
makeCakeLater(); // /Made a banana cake!
makeCakeLater
을 사용하여 원하는 시기에 함수를 호출할 수 있고, 1초 뒤, Made a banana cake!
를 출력할 것입니다.
이와 같이 클로져를 사용해서 사이드이펙트를 방지할 수 있습니다.
(내부 클로져를 호출할 수 있는 함수를 만듬)
Closures and scope
클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하여 유지시키는 함수다.
예시
var a = 10;
var b = 20;
function func1() {
return a + b;
}
func1(); // 30
func1
에서 a
와 b
를 참조하여 실행결과가 30으로 나온다.
그렇다면, func1
은 클로져일까? 유인동님의 함수형 자바스크립트 프로그래밍 책에 의하면 아니다!
우선 func1
이 클로져로 오해받을 수 있는 원인은 Func1
의 스코프 상위에 있는 변수 a
와 b
를 참조한다는 것이다.
하지만 a
와 b
는 func1
에 의해 사라지지 못하는 상황이 아니다. a
와 b
는 글로벌 스코프에서 선언되었고 다른 하위 함수에 참조될 수는 있지만 사라지지는 못한다.
예시
xxxxxxxxxx
function func2() {
var a = 10;
var b = 20;
function func3(c, d) {
return c + d;
}
return func3;
}
var func4 = func2();
func4(5, 7); // 12
func3
을 클로져라고 할 수 있을까? 유인동님의 대답은 역시 No다.
앞의 예시와 다르게 func3
은 func2
스코프 안에서 생성되었고, c
, d
는 func3
에서 정의되어 사용하고있다.
하지만 자신이 생성될 때의 스코프가 알고 있는 변수 a
, b
는 사용하지 않았으므로 func3
이 생성될 때, 기억하고 있는 변수는 없다. 따라서 클로저가 아니다.
예시
function func4() {
var a = 10;
var b = 20;
function func5() {
return a + b;
}
return func5();
}
func4(); // 30
위 예시에는 드디어 클로져가 있을까? 이번에도... No다. 하지만 정확한 표현은 있었다.이다.
func4
가 실행된 후, a
와 b
가 할당된다. 그리고 func5
는 a
와 b
를 사용해서 return
값을 출력하므로 func5
는 자신이 생성된 환경을 기억하고 있었다고 할 수 있다.
예시
xxxxxxxxxx
function func6() {
var a = 10;
function func7(b) {
return a + b;
}
return func7;
}
var func8 = func6();
func8(20); // 30
func8(10); // 20
감격스럽게도 func7
은 클로져이다.
func7
이 a
를 사용하기에 a
를 기억해야하고 func7
이 func8
에 담겼기 때문에 클로져가 되었다. func6
를 실행하였을 때, func6
의 지역변수는 모두 사라져야 하지만 func6
이 끝난 이후에더 func7
이 func6
의 a
를 기억하는 클로져가 되었기 때문에 a
는 사라지지 않는다.
따라서 func8
을 실행할 때마다 a
는 새로운 변수 b
와 함께 사용되어 결과를 만든다.
Closure and Memory Leak
클로져가 될까봐 개발자들이 함수안에서 보조함수들을 선언하기 꺼려하였었습니다.
이는 함수안에서 함수를 정의하면 클로져가 된다. 라고 생각했기 때문이었습니다.
클로져가 된다면 특정 변수(위의 예시에서는 a
)를 기억해야하고 이는 메모리 누수를 야기한다고 생각했기 때문입니다.
하지만 우선 Closure and scope
의 세 번째 예시처럼 함수내부에 함수를 정의하여 외부변수를 참조하는 것만으로 클로져가 되지 않습니다.
네 번째 예시처럼 클로져가 되더라도 a
는 func8
을 실행할 때, 한 번 생겨날 뿐, 계속해서 생기거나 하지 않습니다. (어떻게 이렇게 되는지 알고싶으시면 사파리, 크롬등의 최적화를 알아보시면 됩니다.)
Closure and timing
function func9() {
var a = 10;
function func10(c) {
return a + b + c;
}
var b = 20;
return func10;
}
var func11 = func9();
func11(30); // 60
func11(30)
의 결과는 놀랍게도 60
이 출력된다. 이는 클로져는 자신이 생성될 때의 스코프에서 알 수 있었던 변수를 기억하는함수이기 때문이다. 여기서 때는 생각보다 길다.
클로져는 자신이 생성되는 스코프의 모든 라인, 어느 곳에서 선언된 변수든지 참조하고 기억할 수 있다.
클로져는 자신이 생성되는 스코프의 실행 컨텍스트에서 만들어졌거나 알 수 있었던 변수 중 언젠가 자신이 실행될 때 사용할 변수들만 기억하는 함수이다. 클로져가 기억하는 변수의 값은 언제든지 남이나 자신에 의해 변경될 수 있다.
'Language > JavaScript' 카테고리의 다른 글
자바스크립트 Array (배열) (0) | 2019.09.18 |
---|---|
자바스크립트 고차함수 (0) | 2019.08.27 |
자바스크립트 스코프 (0) | 2019.08.03 |
자바스크립트 일급함수 (0) | 2019.08.02 |
자바스크립트의 프로토타입 (0) | 2019.05.24 |