var x = 'global'
function ex(){
var x = 'local'
x = 'change'
}
ex();
console.log(x) // globalex 함수 바깥의 x는 전역변수 , ex 함수의 안의 x는 지역변수. 위의 상황에서 지역변수는 아무리 해도 전역변수에 영향을 끼칠 수 없다. 바로 스코프 때문이다.
let 키워드로 선언된 변수를 전역 변수로 사용하는 경우, let 전역 변수는 전역객체의 프로퍼티가 아니다.
let foo = 123;
console.log(window.foo) // undefined
var bar = 345;
console.log(window.bar) // 345전역코드에서 let은 보이지 않는 개념인 블록 레벨에 저장된다.
{
let a = 1
console.log(a) // 1
}또 블록 단위에서도 호이스팅이 일어나기 때문에 아래와 같은 예시에서 참조에러가 발생한다.
let foo = 1; // 전역 변수
{
console.log(foo); // ReferenceError: foo is not defined
let foo = 2; // 지역 변수
}var x = 'global'
function ex(){
x = 'change'
}
ex()
console.log(x) // change자바스크립트는 변수의 범위를 호출한 함수의 활성객체 부터 전역객체까지 점차 넓혀가며 찾는다. 함수 ex의 범위 안에 x가 없기 때문에 현재 실행컨텍스트의 스코프체인을 통해서 상위 컨텍스트( 여기서는 GEC )를 찾게된다.
var gVar = 'donguk'
function log(){
gVar = 'dongdong'
}
function wrapper(){
var gVar = 'ukuk'
log()
}
wrapper()JS에서는 함수를 실행할 때가 아닌 선언할 때 스코프가 생기기 때문에 위 log 함수 내부의 gVar 변수는 글로벌 객체의 gVar를 참조한다. ( log 함수의 실행 컨텍스트의 스코프체인에 1번째 인덱스에 wrapper함수가 아닌 global execution context가 있기 때문. )
무분별한 전역변수 사용은 여러 자바스크립트 파일이 참조하면서 오염될 가능성이 높다.
이를 해결하기 위한 방법은? 네임스페이스를 만든다.
var obj = {
x: 'local'
}위와 같이 객체로 오염될 가능성이 있는 변수를 종속시킨다. 하지만 위 방법도 obj.x = 'hi' 와 같은 코드에 무너질 가능성이 높다.
var another = function() {
var x = 'local'
function y(){
console.log(x)
}
return { y }
}
var newScope = another()위와같은 방법으로 문제를 해결할 수 있다. another 함수를 실행시키면 newScope라는 네임스페이스를 통해 y를 참조할 수 있다. 하지만 x는 접근할 수 없다. ( x는 비공개 변수 , y는 공개 변수 )
위의 코드를 더 간단하게 할 수 있다. ( 함수 표현식만 가능하다.)
var newScope = (function(){
var x = 'local'
return {
y: function(){
console.log(x)
}
}
})()위와 같은 패턴을 즉시 호출 함수 표현식이라고 하고, 모듈 패턴이라고 한다.( 많은 라이브러리에서 사용하고 있다. ) 함수를 선언하자마자 바로 실행하는 개념이다. 즉시 호출 함수 표현식은 비공개 변수를 만들어주는 기능을 한다.
즉시 호출 함수 표현식을 사용하면 네임스페이스를 확보할 수 있고, 비공개 변수를 만들 수 있다
그 외에도, 변수에 별칭을 줘서 전역 변수가 오염되는 것을 막을 수 있다.
(function($){...})(jQuery)같은 이야기 이지만 변수의 스코프를 제한할 때 유용하다. 예시로,
for (var i=0; i<5; i++) { setTimeout(()=> console.log(i), 1000) }위 코드의 경우
setTimeout함수의 1초가 끝난 후 콜백 함수들이 태스크큐에서 나와서 하나씩 실행될 때 콜백함수의 상위 컨텍스트인 GEC에 저장된i의 값은 5 이기 때문에 5를 5번 출력하게 된다. 만약 0,1,2,3,4를 출력하고 싶다면?사실 변수를
var에서let으로 바꿔주면 참조 스코프가{}로 묶여서 해결할 수 있다. 하지만! 즉시 호출 함수 표현식으로도 할 수 있다.for (var i=0; i<5; i++) { (function (j){ setTimeout(() => console.log(j), 1000) })(i) }변수의 범위를 즉시 호출 함수 내부로 묶어서
setTimeout의 콜백함수가 실행될 때 참조하는 j의 값은 해당 시점의 i를 참조하게 된다.위에서
let으로 해결하는 방법도 적어보면,for (let i=0; i<5; i++) { setTimeout(()=> console.log(i), 1000) }for 루프의
leti 는 for loop에서만 유효한 지역변수이다 . 또한i는 for 루프가 종료되더라도 변수 i를 참조하고 있는 함수가 존재하는 한 계속 유지된다. (블록단위로 지역변수i가 5개 생성되겠꾼 ㅎㅎㅎㅎ)