- Published on
클로저 Deep Dive
- Authors
- Name
해당 글은 You don't konw JS의 클로저에 대한 글을 정리한 글입니다.
https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/scope-closures/ch6.md
클로저란?
클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다. 함수가 선언될 당시의 외부 변수를 기억하고 있고 함수를 선언할 때 만들어진다.
그러면 왜 변수는 전역 범위에 선언하는 것이 좋지않을까? 아마 전역변수가 위험하다는것은 알고있으나 정말로 왜 불편하고 위험한지를 알아볼 필요는있다.
전역변수의 문제점
- 이름 충돌
프로그램의 서로 다른 부분에서 공통적이고 유용한 변수와 함수 이름을 사용하지만 식별자가 하나의 공유범위에서 오는 경우 이름의 충돌이 발생할 가능성이 농후하다. 그로인해 버그가 발생할 가능성이 있다. - 예기치 않은 동작
전역변수는 어디서든지 읽고 쓸 수 있기 때문에 의도치 않은 곳에서 값이 변경될 수 있다. 그로인해 예기치 않은 동작이 발생할 수 있다. 예를들어 일부에서 사용되고있는 변수와 함수를 노출하면 다른 개발자가 의도하지 않은 방식으로 사용할 수 있다. - 의도하지 않은 종속성
변수와 함수를 불필요하게 노출하면 다른 개발자가 비공개부분을 사용하고 의존하도록 유도하게된다. 현재는 프로그램이 잘 동작하지만 나중에 비공개 부분이 변경되면 의존성이 있는 코드도 함께 변경해야한다. 그로인해 예기치 않은 버그가 발생할 수 있다.
변수와 함수 범위 지정에 적용되는 일반적인 원칙은 최소 권한의 원칙이다. 최소 권한의 원칙은 프로그램의 나머지 부분에 영향을 미치지 않는 한 최소한의 권한만 부여하는 것이다. 전역변수는 최소한의 권한만 부여하지 않는다. 전역변수는 프로그램의 모든 부분에서 읽고 쓸 수 있기 때문이다.
function diff(x, y) {
if (x > y) {
let tmp = x
x = y
y = tmp
}
return y - x
}
diff(3, 7) // 4
diff(7, 5) // 2
위의 코드 예시에서는 tmp 변수가 if 블록 안에 있는지, 함수 스코프 안에있는지는 중요하지 않은것 같지만 전역 변수가 되어서는 안된다. tmp 변수는 가능한 범위 내에 있어야한다. 그렇지 않으면 다른 코드에서 tmp 변수를 의도치 않게 사용할 수 있다.
따라서 let을 사용하여 블록스코프를 만들어서 tmp 변수를 블록 스코프 안에 넣어야한다.
함수 스코프에 숨기기
이제 가능한 가장 낮은(가장 깊게 중첩된) 범위에 변수와 함수 선언을 숨기는것이 중요한지 알았다. 하지만 어떻게 해야할까...?
블록 레벨 스코프인 let 키워드를 보았다. 하지만 let은 블록 스코프를 만들어주지만 함수 스코프를 만들어주지는 않는다. 따라서 함수 스코프를 만들어주는 방법을 알아보자.
var cache = {}
function factorial(x) {
if (x < 2) return 1
if (!(x in cache)) {
cache[x] = x * factorial(x - 1)
}
return cache[x]
}
factorial(6) // 720
cache
/**
* {
* 2: 2,
* 3: 6,
* 4: 24,
* 5: 120,
* 6: 720
* }
*/
factorial(7) // 5040
위의 코드에서 cache 변수는 factorial 함수 스코프 안에 있다. 따라서 factorial 함수 스코프 안에서만 접근할 수 있다. 그러므로 factorial 함수 스코프 안에서만 cache 변수를 사용할 수 있다.
factorial은 재귀함수다. 즉 factorial 함수가 자기 자신을 호출한다. factorial 함수가 자기 자신을 호출할 때마다 cache 변수에 새로운 프로퍼티를 추가한다. 그리고 factorial 함수가 종료되면 cache 변수는 계속 살아있다. 왜냐하면 cache 변수는 factorial 함수 스코프 안에 있기 때문이다.