키워드: 스코프
IIFE
let
const
- 스코프는 버블이다. 무슨 말이냐면,
- 스코프 검색은 항상 실행 시점에서 가장 안쪽 스코프에서 시작하여 최초 목표와 일치하는 대상을 찾으면 멈추고, 그 전까지는 바깥/위로 올라가면서 수행한다.
- 함수만 버블을 만들까? 자바스크립트의 다른 자료 구조는 스코프 버블을 생성하지 못할까?
- 결론부터 이야기하면, 함수는 결코 유일한 스코프 단위가 아니다.
- 함수에 대한 전통적인 개념은 다음과 같다:
- 함수를 선언하고 그 안에 코드를 넣는다.
- 작성한 코드에서 임의 부분을 함수 선언문으로 감싼다. 이는 해당 코드를 숨기는 효과를 낸다.
- 스코프를 이용해 숨기는 방식을 사용하는 이유는,
- 소프트웨어 디자인 원칙인 '최소 권한의 원칙'과 관련이 있다. 이 원칙은 모듈/객체의 API와 같은 소프트웨어를 설계할 때 필요한 것만 최소한으로 남기고 나머지는 숨겨야 한다는 것이다.
- 만일 모든 변수와 함수가 글로벌 스코프에 존재한다면 어느 중첩된 하위 스코프에서도 이들에 접근할 수 있다. 이는 최소의 원칙을 어기는 것이고, 코드를 적절하게 사용했을 때 접근할 필요가 없어 비공개로 남겨둬야 할 많은 변수나 함수를 노출시키게 된다.
- 변수와 함수를 스코프 안에 숨기는 것의 또 다른 장점은 같은 이름을 가졌지만 다른 용도를 가진 두 확인자가 충돌하는 것을 피할 수 있다는 점이다. 소프트웨어 설계를 하다 보면 자연스럽게 같은 확인자 이름을 사용하게 되므로 스코프를 이용해서 내부에 선언문을 숨기는 것이 가장 좋은, 그리고 유일한 선택지다.
- 어떤 라이브러리는 글로벌 스코프에 하나의 고유한 이름을 가지는 객체 선언문을 생성한다. 이후 객체는 해당 라이브러리의 네임스페이스로 이용된다.
- 네임스페이스를 통해 최상위 스코프의 확인자가 아니라 속성 형태로 라이브러리의 모든 기능이 노출된다.
- 좀더 현대적인 충돌 방지 옵션으로는 다양한 의존성 관리자를 이용한 모듈 접근법이 있다.
- 모듈을 사용하면 어떤 라이브러리도 확인자를 글로벌 스코프에 추가할 필요 없이, 특정 스코프로부터 의존성 관리자를 이용한 다양한 명시적인 방법으로 확인자를 가져와 사용할 수 있다.
var a = 2;
function foo() {
var a = 3;
console.log(a); // 3
}
foo();
console.log(a); // 2
- 위의 방식은 이상적이지 않으며, 다음의 두 문제를 갖는다:
foo()
라는 이름의 함수를 선언해야 한다. 그저 또다른 값을 할당한 변수a
를 숨기고 호출하기 위해?!- 또한
foo
함수를 직접 호출해야만 실제 감싼 코드를 실행할 수 있다.
- 함수를 이름없이 선언하고 자동으로 실행할 수 있다면? 다행히도 그런 방법이 있다:
var a = 2;
(function foo() {
var a = 3;
console.log(a); // 3
})();
console.log(a); // 2
- 위에서
foo
함수는 함수 선언문이 아니라 표현식이다.- 선언문과 표현식의 중요한 차이는 함수 이름이 어디의 확인자로 묶이느냐에 있다. 첫번째에서는 함수 이름
foo
가 함수를 둘러싼 스코프에 묶여있다. 반면 두번째에서 함수 이름foo
는 함수 자신의 내부 스코프에 묶였다.
- 선언문과 표현식의 중요한 차이는 함수 이름이 어디의 확인자로 묶이느냐에 있다. 첫번째에서는 함수 이름
- 일단 함수 선언문에서는 이름이 빠져서는 안된다.
- 함수 표현식은 익명으로 만들 수 있다.
- 익명 함수 표현식은 빠르고 쉽게 입력할 수 있어 많이 권장된다. 그러나 기억해야 할 몇 가지 단점도 있다 (이름이 없다는 것이 주는 단점):
- 익명 함수는 스택 추적시 표시할 이름이 없어서 디버깅이 더 어려울 수 있다.
- 이름 없이 함수 스스로 재귀 호출을 하려면 불행히도 폐기 예정인
arguments.callee
참조가 필요하다. - 이름은 보통 쉽게 이해하고 읽을 수 있는 코드 작성에 도움이 되는데, 익명 함수는 이런 이름을 생략한다. 기능을 잘 나타내는 이름은 해당 코드를 그 자체로 설명하는데 도움이 된다.
- 그러니까 결론적으로, 함수 표현식을 사용할 땐 이름을 항상 쓰는 것이 가장 좋다
- 익명 함수 표현식은 빠르고 쉽게 입력할 수 있어 많이 권장된다. 그러나 기억해야 할 몇 가지 단점도 있다 (이름이 없다는 것이 주는 단점):
var a = 2;
(function foo() {
var a = 3;
console.log(a); // 3
})();
console.log(a); // 2
- 위의 예시
foo
함수에서 (1) 함수를 둘러싼 첫번째()
는 함수를 표현식으로 바꾸고, (2) 두번째()
는 함수를 실행시킨다. - 이런 패턴은 굉장히 흔해서 개발자 커뮤니티에서는 몇년 전 이것을 부르는 즉시 호출 함수 표현식 IIFE라는 용어를 정하기도 했다.
- Immediately Invoked Function Expression
- 이미 가장 앞에서 이야기했지만, 함수가 유일한 스코프 단위가 아니다: 블록 스코프가 있다.
- 블록 스코프의 목적은, 변수를 최대한 사용처 가까이에서 최대한 작은 유효 범위를 갖도록 선언하는 것이다.
- 앞서 말한 '최소 권한 노출의 원칙'을 확장하여 정보를 함수 안에 숨기고, 나아가 정보를 코드 블록 안에 숨기기 위한 도구다.
with
문 안에 생성된 객체는 바깥 스코프에 영향을 주지 않고 존재하는데 - 지양해야 할 구조다.try/catch
에서catch
부분에서 선언된 변수는catch
블록 스코프에만 속한다.
ES6
에서let
키워드가 도입됐다.let
키워드는 선언된 변수를 둘러싼 아무 블록의 스코프에 붙인다.let
을 이용해 변수를 현재 블록에 붙이는 것은 약간 비명시적이다.let
을 사용한 선언문은 속하는 스코프에서 호이스팅 효과를 받지 않는다. 호이스팅이 뭐냐면,- 호이스팅은 선언문이 어디에서 선언됐든 속하는 스코프 전체에서 존재하는 것처럼 취급되는 작용을 말한다.
- 블록 스코프는 엔진에게 어떤 변수가 더는 필요 없다는 사실을 더 명료하게 알려준다. 예를 들어,
var bigData = { ... }; // 아주 큰 데이터라고 치자
process(bigData);
var btn = document.getElementById("my_btn");
btn.addEventListener("click", function click(evt) {
console.log("clicked!");
});
- 위의 예시에서 보면,
process
함수가 실행된 이후, 아주 큰bigData
변수는 더 이상 쓸모가 없다. 버튼의 클릭 이벤트 핸들러에서도 사용되지 않는다.- 그러므로 아래와 같이 블록 스코프를 이용하면, 엔진에게
bigData
가 더는 필요없다는 걸 알려줄 수 있다.{ let bigData = { ... }; process(bigData); } var btn = document.getElementById("my_btn"); btn.addEventListener("click", function click(evt) { console.log("clicked!"); });
- 위와 같이 명시적으로 블록을 선언하여 변수의 영역을 한정하는 것은 효과적인 코딩 방식이다.
- 그러므로 아래와 같이 블록 스코프를 이용하면, 엔진에게
ES6
에서는let
과 함께const
키워드도 추가됐다.- 이 역시 블록 스코프를 생성하지만, 다른 점이라면 선언한 값이 고정되는 상수라는 점이다.
- 따라서 선언 후 그 값을 변경하려고 하면 에러가 발생한다.
- 함수는 스코프를 이루는 가장 흔한 단위이다.
- 함수 안에 선언된 변수, 함수 등은 다른 스코프로부터 숨겨진 것이다.
- 함수가 유일한 스코프 단위가 아니다. 블록 스코프가 있다.
try ... catch
문에서catch
문은 블록 스코프를 갖는다.let
으로 임의의 코드 블록 안에 변수를 선언할 수 있게 됐다.- 그러나 많이들 착각하는 것과 달리 블록 스코프는
var
함수 스코프를 완전히 대체할 수 없다. 호이스팅이 되는지 안되는지 여부에 차이가 있다.
- 그러나 많이들 착각하는 것과 달리 블록 스코프는
- 1권이 거의 끝나간다. 이 책 내용이 최신의 내용으로 업그레이드 됐다면 특히 이번 단원의 내용이 많이 달라졌지 않을까 생각이 들었다.
- 한번 읽으면서 눈에 들어오는 부분에 밑줄을 치고, 이후에 그 내용 위주로 마크다운에 정리를 했더니 짧게 두번 반복해서 그런지 내용이 머리에 더 잘 들어왔다.
- 입춘이다! 벌써 봄. 봄이 한창일 때 이 스터디가 끝날 텐데 기대가 된다.