Scope란 번역해보자면 '범위'라는 뜻을 가지고 있다. 변수에 접근할 수 있는지에 대한 규칙이자 코드의 유효범위라고 볼 수 있다. 즉, 스코프는 변수 접근 규칙에 따른 유효 범위이다.
스코프에는 2가지 종류가 있는데, 전역 스코프(global scope)와 지역 스코프(local scope)이다.
전역 스코프(global scope)
let callBy = 'Hey'
로컬스코프와 글로벌 스코프의 차이
- 안쪽 스코프에서 바깥 변수/함수를 접근하는 것은 가능
- 바깥쪽 스코프에서 안쪽 변수/함수를 접근하는 것은 불가능
지역 스코프(local scope)
function callByName() { let name = 'Hae'; return callBy + ' ' + name; }
- 스코프는 중첩이 가능하다 함수 안에 함수를 넣을 수 있다 - 글로벌 스코프는 최상단의 스코프로 전역변수는 어디서든 접근이 가능하다 - 지역변수는 함수 내에서 전역변수보다 더 높은 우선순위를 가진다
let callBy = 'Hey' // 전역변수
function callByName() {
let name = 'Hae'; // 지역변수
return callBy + ' ' + name;
}
callByName(); // => 'Hey Hae'
name; // => ReferenceError > name은 지역함수내에 있는 지역변수이므로 전역스코프에서 접근할 수 없다.
Block : 중괄호({})로 시작하고 끝나는 단위
Javascript는 기본적으로, 함수단위로 자신만의 Scope를 가진다.(var키워드가 그렇다)
그러나, Block단위로 Scope를 구분하게 되면 예측하기 쉬운코드를 작성할 수 있다.(그래서 요즘은 let키워드를 통한 선언을 많이 한다.)
for(var i = 0; i<5; i++) {
console.log(i); // => i가 5번 찍힌다.
}
console.log('final i:', i); // 5
//for문 안의 var키워드를 통해 선언된 i는 block범위를 벗어나도 같은 function scope 에서는 사용이 가능한다.
for(let i = 0; i<5; i++) {
console.log(i); // => i가 5번 찍힌다.
}
console.log('final i:', i) // =>ReferenceError
// For 문 안의 let키워드를 통해 선언된 i는 블럭을 벗어나는 순간 사용할 수 없기 때문에 블럭(중괄호)밖의 i는 변수를 찾을수 없게 된다
let | const | var | |
---|---|---|---|
유효범위 | Block Scope | Block Scope | Function Scope |
값 재정의 | 가능 | 불가능 | 가능 |
재선언 | 불가능 | 불가능 | 가능 |
MDN에서는 Closure를 다음과 같이 정의하고 있다.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function.
무슨 얘기인지 잘 모르겠으니 한국어로 번역된 MDN을 보자
클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다.
무슨소리인지 더 모르겠다. 역시 이래서 원서를 읽으라고 하는 건지도 모르겠다.
영어 MDN을 해석해보고 다른 곳에서 더 찾아본 결과
라고 한다.
클로저함수는 지역변수, 외부함수의 변수, 전역변수 모두에 접근이 가능하다
function adder(x) {
return function(y) {
return x + y;
}
}
adder(2)(3); // 5
let add100 = adder(100);
add100(2); // 102 => x의 값을 고정해놓고 재사용 할 수 있다.
add100(10); // 110
let add5 = adder(5);
add5(2); //7
function htmlMaker(tag) {
let strtTag = '<' + tag + '>';
let endTag = '</' + tag + '>';
return function(content) {
return startTag + content + endTag
}
}
let divMaker = htmlMaker('div');
divMaker('Hae') // <div>Hae</div>
divMaker('Bae') // <div>Bae</div>
let h1Maker = htmlMaker('h1');
h1Maker('headline'); // <h1>headline</h1>
function makeCounter() {
let privateCounter = 0;
return {
increment : function() {
privateCounter++;
},
decrement: function() {
privateCounter--;
},
getValue: function() {
return privateCounter;
}
}
}
let counter1 = makeCounter();
counter1.increment();
counter1.increment();
counter1.getValue(); //2
let counter2 = makeCounter();
counter2.increment();
counter2.decrement();
counter2.increment();
counter2.getValue(); // 1
// 두 카운터에 각기 다른 privateCounter를 다루면서, privateCounter를 밖으로 노출시키지 않는다.
사실 클로저 모듈패턴은 아직 정확하게 이해하지 못했다. 앞으로 점점 자바스크립트를 배워가면서 이해하게 될 것이라 생각된다.