요약
이번 장에서는, 변수를 조직하기 위해 왜 다양한 스코프를 이용해야 되는지 알아본다.
Least Exposure
POLE(Principle of Least Privilege) => 최소권한 원칙
시스템의 구성요소가 최소한의 권한, 최소한의 접근권한, 최소한의 노출로 동작되도록 설계되어야 함을 나타냄.
그럴수록 하나의 구성요소의 결함 또는 실패가 전체 시스템에 미치는 영향이 적어짐
=> 그래서 블록을 사용하여 스코프를 써야 한다!!
왜 전역에 모든 변수를 배치하면 안 좋을까?
1. 이름충돌
프로그램의 다른 부분에서 이름이 충돌남!
2. 예상치 못한 작동
private한 함수를 사용하여 개인정보가 노출될 수 있음
3. 의도하지 않은 의존성
어떤 코드가 숫자배열에 의존하고 있고, 나중에 이 숫자배열을 손봐야 할 때 모든 코드를 수정해야 할 수 있음.
이러한 문제를 예방하기 위해 스코프 안에 변수를 사용하는 게 좋다 (let을 써서)
Scoping with Blocks
- 모든 블록이 스코프가 되는 것은 아님
1. 객체 리터럴에서 중괄호는 스코프가 아님
2. 클래스에서 body 중괄호도 스코프가 아님
3. 함수의 중괄호는 스코프임
4. switch문의 스코프는 스코프가 아님
if (somethingHappened) {
// this is a block, but not a scope
{
// this is both a block and an
// explicit scope
let msg = somethingHappened.message();
notifyOthers(msg);
}
// ..
recoverFromSomething();
}
=> 대부분의 개발자는 msg를 if블록으로 스코핑 하고 넘어갈 것임. 그러나 msg가 다른 데서 안 쓰인다면 이처럼 블록스코프 처리해 주는 게 POLE를 따르는 것이라 할 수 있음.
function getNextMonthStart(dateStr) {
var nextMonth, year;
{
let curMonth;
[ , year, curMonth ] = dateStr.match(
/(\d{4})-(\d{2})-\d{2}/
) || [];
nextMonth = (Number(curMonth) % 12) + 1;
}
if (nextMonth == 1) {
year++;
}
return `${ year }-${
String(nextMonth).padStart(2,"0")
}-01`;
}
getNextMonthStart("2019-12-25"); // 2020-01-01
이처럼 curMonth가 쓰이는 곳에 한해서 스코프를 지정하는 것.
var and let
var은 js초기부터 함수전체에 속하는 변수를 나타내는 신호였음.
전역범위를 나타내고 싶을 때는 var을 사용하고, 블록범위를 나타내고 싶을 때 let을 사용하면 가독성이 좋음.
=> var을 지양하고 let을 사용하는 게 일반적이지만, 저자는 var 또한 여전히 사용가치가 있다고 주장함.
Where To let?
let을 어디다가 써야 할까?
선언이 블록범위라면 let을 쓰고, 함수 범위라면 var를 써라(저자의견)
function diff(x,y) {
if (x > y) {
// `tmp` is still function-scoped, but
// the placement here semantically
// signals block-scoping
var tmp = x;
x = y;
y = tmp;
}
return y - x;
}
es6이전에는 let이 없었기 때문에 배치를 통해 블록범위를 나타냈음
What's the Catch?
try-catch는 es3에서 추가된 문법임.
try {
doesntExist();
}
catch (err) {
console.log(err);
// ReferenceError: 'doesntExist' is not defined
// ^^^^ message printed from the caught exception
let onlyHere = true;
var outerVariable = true;
}
console.log(outerVariable); // true
console.log(err);
// ReferenceError: 'err' is not defined
// ^^^^ this is another thrown (uncaught) exception
여기서 catch안의 err는 블록스코프를 가지며, var 또한 유효함.
Function Declarations in Blocks (FiB)
블록 안의 함수는 어떠한 스코프를 가질까?
if (false) {
function ask() {
console.log("Does this run?");
}
}
ask();
이 결과는 js환경에 따라 다르다....
대부분의 브라우저에서는 ask() 호출이 TypeError와 함께 실패한다.
왜냐하면 if문이 실행되지 않기 때문에 ask가 undefined 상태임.
if (true) {
function ask() {
console.log("Am I called?");
}
}
if (true) {
function ask() {
console.log("Or what about me?");
}
}
for (let i = 0; i < 5; i++) {
function ask() {
console.log("Or is it one of these?");
}
}
ask();
function ask() {
console.log("Wait, maybe, it's this one?");
}
이 경우 함수들이 호이스팅 돼서 Wait,~~이 출력될 것 같지만, 아니다...(실제는 OR IS IT ONE~~ 출력)
이처럼 FIB는 이상한 에지 케이스가 많다.
즉, FIB의 모호성을 피하는 가장 좋은 방법은 FIB를 피하는 것임.
=> 항상 함수의 최상위 범위 OR 전역범위에 선언하라!!!!
이러한 예제들에서 주목할 것은, 함수 표현식이 아니라 함수선언을 IF문 내에 배치한 것.
즉 블록 안에서 function 선언을 피해라
프로그램이 올바르게 작동하고 있더라도 다른 js환경에서 문제가 발생할 수 있다.
FIB는 피하자
느낀 점
이 챕터를 읽으며 let과 var을 함께 쓰라는 저자의 말이 좀 참신했다. 그동안 정보를 그냥 받아들이고 있지는 않았는지 생각해 보게 되는 계기였음....
그리고 스코프와 블록의 미묘한 차이에 대해 배울 수 있었고
FIB는 생각도 못하고 있었는데 이 챕터에서 인사이트를 얻은 것 같다.
'공부 정리 > You Don't Know Js' 카테고리의 다른 글
[YDKJ] Scope & Closures - The Module Pattern (1) | 2023.06.05 |
---|---|
[YDKJ] Scope & Closures - Using Closures (0) | 2023.05.29 |
[YDKJ] Scope & Closures - The (Not So) Secret Lifecycle of Variables (0) | 2023.05.22 |
[YDKJ] Scope & Closures - Around the Global Scope (0) | 2023.05.18 |
[YDKJ] Scope & Closures - The Scope Chain (1) | 2023.05.09 |
댓글