우린 스코프를 이미 알고있다.
함수 매개변수는 내부에서만 참조 가능하다.
이것이 스코프다. 참조가 유효한 범위를 알려준다.
=> 모든 식별자는 선언된 위치에 의해 다른 코드가 자신을 참조할수 있는 유효범위를 가진다.
폴더와 파일을 생각하면 편하다.
게임
폴더에 롤
이라는 파일이 있고, 음식
이라는 폴더에 롤
이라는 파일이 있다.
충돌이 나지 않는다. 다른 네임 스페이스를 지니기때문이다.
=> 스코프는 곧 네임 스페이스다.
같은 식별자를 JS는 어떻게 참조할까? 간단하게 알아보자.
const x = "global";
function foo() {
const x = "local";
console.log(x); // 1번호출
}
foo();
console.log(x)//2번호출
// ...1번 local, 2번 global
스코프에 대해 몰라도, 결과는 알것이다.
JS엔진은 스코프를 통해 어떤 변수를 참조할지 정했다.
스코프는 JS엔진이 식별자를 검색할때 사용하는 규칙 이라 여겨도 무방하다.
간단하게 전역,지역으로 나뉜다. 전역이 아닌 모든 스코프는 지역스코프다.
전역변수는 어디서든 참조 할 수 있고, 지역 변수는 자신의 스코프 + 하위 스코프에서 참조 가능하다.
조금 더 깊게 가보자.
const x = "global";
function outer(){
const x = "localOfOuter";
console.log(x);
function inner(){
const x = "localOfInner";
console.log(x);
}
inner();
}
outer();
console.log(x);
//localOfOuter, localOfInner, global 순으로 출력된다.
왜 이런 결과가 발생할까?
위에서도 말했지만, 스코프때문이다. 정확히는 스코프를 무조건상위방향으로 타고가며 선언된 변수를 검색하기때문이다.
이를 스코프 체인이라 함.
JS는 스코프를 갖고있는 어떠한 자료구조를 실제로 생성한다.=> Lexical Environment
참고로 무조건위로 타고가기 때문에, 하위 스코프의 식별자는 참조 불가
var는 함수 내부에서만 스코프를 가진다. 나머지는 전역스코프.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar(){
console.log(x);
}
foo();
bar();
//...전역변수인 1을 두번출력.
bar()
의 상위스코프는 무엇일까? 전역? 아니면 foo()
?
foo()
라면, 호출한 위치에서 결정되고
전역이라면 선언한 위치에서 결정된다.
JS의 상위 스코프는 선언한 위치에서 결정된다.
참고로 렉시컬 스코프는 클로저와 깊은 연관이 있다. 나중에 살펴봄.
호이스팅은 사실 스코프 단위로 일어난다.
따라서 함수 내부 선언된 식별자는 함수가 호출되고 나서야 호이스팅된다.
var x = "global";
function foo(){
console.log(x); // 지역변수 x가 호이스팅되어 undefined
var x = "local";
}
foo();
console.log(x); // 전역변수 x를 출력. global
=> 함수 내부에 선언된 지역 변수의 생명주기는 함수와 같다.
단, 예외도 있다. 누군가가 계속 지역변수를 참조하고 있다면...
변수는 태어나고 임무를 다한 뒤 죽는다. 마치 생물의 삶처럼 생명주기가 있다.
하지만 전역변수는 죽지 않는다(웹페이지를 닫기 전까지).
=> var로 선언한 전역변수는 전역 객체의 프로퍼티가 된다.
이는 메모리 공간의 낭비다.
다른 문제점도 있다.
그래 잘알았다.
그러면 전역변수를 안쓰면 되겠구만?
()
괄호로 감싸고 바로 실행한다.(function(){
var foo = 10;
//...
}());
console.log(foo); // foo is not defined...
var Counter = (function() {
var num = 0;
//외부로 공개할 데이터를 객체로 반환한다.
return {
increase() {
return ++num;
},
decrease() {
return --num;
}
};
}());
//공개하지 않은 데이터는 외부로 노출되지 않음
console.log(Counter.num) // undefined
console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0
<script type="module" src="src.mjs"></script>
확장자는 mjs
를 권장한단다.
var는 안쓰는게 좋다.
for
문 안에 선언해도 밖에서 참조가 가능하다....이런 이유때문이다. 그래서 let이 등장했다.
const는 상수를 선언하기 위해 등장했다. 다만 반드시 상수만을 위해 사용하는 건 아님.
이눔의 특징은...
let preTaxPrice = 100;
// 0.1의 의미를 명확히 하자
let afterTaxPrice = preTaxPrice + (preTaxPrice * 0.1);
---
const TAX_RATE = 0.1;
let preTaxPrice = 100;
let afterTaxPrice = preTaxPrice + (preTaxPrice * Tax_RATE);
최대한 const
로 선언한 뒤, 바뀔때 let
을 사용해도 늦지 않는다.
특히 재할당이 필요없는 객체는 const
를 사용하자.
내부 프로퍼티는 바꿀수 있음 => 불변성
알고있던 내용이지만 확실히 알게되서 기초가 든든해진 느낌이다.
또한 모듈쪽을 아직 자세히 몰라서 공부해보고싶다.