변수이름 , 함수이름 ,클래스 이름과 같은 식별자가
본인이 선언된 위치에 다라 다른 코드에서 자신이 참조될 수 있을지 없을지 결정되는 것
function add (x, y){
console.log(x, y); //선언된 위치에 다라 본인의 유효범위가 결정됨 이것이 스코프
return x + y
}
add(3, 6)
console.log(x, y) // x, y 선언 유효범위 밖이다 Error
어떠한 변수가 전역에서 선언되면 전역스코프를 지역에서 갖게 되면 지역스코프를 갖게된다
어떠한 코드가 실행이 될때 참조하려는 변수가 전역엔 없는데 지역엔 있고
지역엔 있는데 전역에도 있고? 여기도 저기에도 있다면 이 코드는 어떤 변수를 참조할까요?
어떤걸 참조할 지 알고 싶다면 스코프체인에 대해 알아볼 필요가 있다
이처럼 함수가 중첩이 된다면 지역스코프도 중첩된다
스코프가 함수의 중첩에 의해 계층적인 구조를 가질 수가 있다!
아래 코드를 보면 변수를 지역 스코프에서 찾을 수 없다면 상위 스코프에서 찾고
상위도 없으면 전역 까지 찾는 스코프체인의 모습을 볼 수 있습니다
var x = '나는 전역 x야'
fuction outer() { // outer 함수안은 outer scope
var y = '나는 outer함수의 지역 y야'
console.log(x); // 나는 전역 x야
console.log(y); // 나는 outer함수의 지역 y야
function inner() { // inner 함수안은 inner scope
var x = '나는 inner함수의 지역 x야'
console.log(x); // 나는 inner함수의 지역 x야
console.log(y); // 나는 outer 함수의 지역 y야
}
inner()
}
outer()
console.log(x)
console.log(y)
ES6 이후로 let과 const 키워드를 사용해 함수뿐만 아니라 if, fo, try문 에서도
스코프를 가질 수 있게 되었다
자바스크립트는 lexical scope를 따르기 때문에 함수는 태어나자마자 상위스코프가 결정이되고
해당함수에 의해 함수 객체가 생성이되면 해당함수 객체는 본인의 상위 스코프를 항상알 수 있게 된다
Lexical scoping은 함수를 어디에서 호출하는지에 따라 범위가 결정되는 것이 아니라 함수를 어디에 선언했는지에 따라 결정된다. (반대로 어디서 호출됐는지에 따라 범위가 결정되는 방식은 Dynamic scoping)이다.
함수는 태어나면서 자신의 내부 슬롯에 상위스코프를 참조를 저장함
const x = 1;
function outer() { outer함수는 inner를 ella에게 반환하면서 생을 마감한다..
const x = 10;
const inner = fuction () {
console.log(x)
};
return inner
}
const ella = outer();
ella(); // 어떤 값이 콘솔에 찍힐까?
outer함수는 inner를 ella에게 반환하면서 생을 마감한다..
outer() 함수 호출이 종료가되면 실행컨텍스트는 stack에서 제거가 된다
outer의 지역변수 x = 10; 또한 제거가 된다
그러나 ella(); 에선 10이라는 값이 출력이된다
죽은 x가 어떻게 다시 살아서 출력이 됐을까???
클로져 때문이다
중첩함수 inner가 이미 생명주기를 마감한 outer함수(외부함수) 지역변수 x를 참조할 수 있다면
이때 inner를 클로저라고 한다
outer는 생명주기를 마감했지만 outer의 렉시컬환경은 남아있어 inner함수는 내부슬롯에 저장된 상위 스코프에 의존하여 outer의 렉시컬 환경을 참조하고 있기때문
클로저에 의해 참조된 변수를 자유변수라고 한다
'클로저' 닫혀있다는 느낌이드는 이름은 함수 본인이 기억하고 있는 자유변수에 의해 갇혀있다라고 생각 할 수 있다
이러한 클로저는 하나의 state가 의도치 않게 변경되지않도록 은닉하고
특정 함수에게만 state변경을 허용하기 위해 사용한다고 한다!