Scope란?
변수에 접근할 수 있는 범위. 식별자가 유효한 범위. 식별자를 검색할 때 사용하는 규칙
참조 대상 식별자를 찾아내기 위한 규칙을 스코프라고 하며 이 규칙에 따라 변수를 식별한다.
식별자는 어떤 값을 구별하여 식별해낼 수 있는 고유한 이름을 말한다. 따라서 유일한 이름을 가지고 있어야한다. 컴퓨터에서 파일 이름을 중복된 이름으로 설정할 수 없는 것처럼 말이다. 하지만 우리는 중복된 파일 이름을 가질 수 있는데 이는 폴더라는 개념이 있기 때문이다.
프로그래밍 언어에서는 스코프가 폴더의 역할을 한다. 유효 범위를 설정해 식별자의 중복 이름 충돌을 막는다.
즉, 스코프 내에서 식별자는 유일해야하나 다른 스코프에서는 같은 이름의 식별자를 사용할 수 있다.
Scope Chain
모든 스코프는 하나의 계층적 구조로 연결되며, 모든 지역 스코프의 최상위 스코프는 전역 스코프
이렇게 스코프가 계층적으로 연결된 것을 스코프 체인이라고 한다.
상위 스코프에서 유효한 변수는 하위 스코프에서 참조 가능하지만 하위 스코프에서 유효한 변수는 상위 스코프가 참조할 수 없다.
Scope의 종류
scope는 유효 범위에 따라 블록 스코프와 함수 스코프 2가지로 나뉜다.
block-level scope 블록 레벨 스코프
코드 블록 내에서 유효한( = 참조할 수 있는) 스코프
중괄호를 기준으로 스코프의 범위가 구분되며, 중괄호 안에서 선언한 변수는 그 중괄호 안에서만 참조가 가능하다.
대부분의 C-family language가 이 규칙을 따르고 있다.
function-level scope 함수 레벨 스코프
함수 코드 블록 내에서 유효한 스코프
function을 기준으로 스코프의 범위 구분하며, 함수 내에서 선언된 변수는 함수 코드 블록 내에서만 참조가 가능하다.
JS의 var는 함수 레벨 스코프를 따름(let은 블록 스코프)
또는 상위 스코프를 결정하는 방식으로 두 가지 스코프로 나눌 수 있다.
Dynamic scope 동적 스코프
함수를 어디서 호출했는지에 따라 상위 스코프 결정
Lexical scope 렉시컬 스코프 / Static scope 정적 스코프
함수를 어디에 선언했는지에 따라 상위 스코프 결정
JS를 비롯한 대부분의 언어는 렉시컬 스코프이다.
scope 규칙
1. 바깥쪽 스코프에서 선언한 변수는 안쪽 스코프에서 사용 ⭕️
반면에, 안쪽에서 선언한 변수는 바깥쪽 스코프에서는 사용 
2. 스코프는 중첩 가능
3. 가장 바깥쪽의 스코프는 전역 스코프(Global scope), 여기서 선언한 변수는 전역 변수
전역이 아닌 스코프는 지역 스코프(Local scope)로 여기서 선언한 변수는 지역 변수
4. 지역 변수는 전역 변수보다 더 높은 우선순위를 가짐
지역 변수가 전역 변수와 동일한 이름의 변수를 가질경우, 지역 변수를 우선함
in JavaScript
function-level scope
JavaScript는 함수 레벨 스코프를 따르고 있다.
함수 선언식이나 함수 표현식을 사용하면 함수 스코프가 만들어진다.
화살표 함수는 function 키워드를 사용하지 않으므로 함수 스코프가 아닌 블록 스코프로 취급된다.
❗️그러나 ECMAScript6부터 도입된 let을 사용하여 블록 레벨 스코프를 사용할 수 있다.
Lexical scope
JavaScript는 렉시컬 스코프를 따른다.
렉시컬 스코프는 함수를 어디서 선언했는지에 따라 상위 스코프가 결정된다.
var x = 1;
function foo() {
var x = 10;
bar();
}
function bar() {
console.log(x);
}
foo(); // 1
bar(); // 1
JavaScript
복사
위 코드에서 bar 함수는 다른 함수나 블록 내에 선언되지 않았으므로 선언 위치에 따라 상위 스코프는 전역이다.
bar 함수 실행 시 1이 출력되므로 foo 함수 내의 bar 함수도 1을 출력하게 된다.
만약 동적 스코프를 따른다고 가정하고 코드를 실행하면 어떨까?
bar 함수의 호출 위치에 따라 상위 스코프는 foo 함수가 된다. 따라서 foo 함수는 10을 출력하게 된다.
위와 같이 함수의 상위 스코프는 함수 정의가 실행될 때 결정됨. 함수 정의가 실행되어 생성된 함수 객체는 함수가 호출될 때마다 상위 스코프를 참조해야하므로 이를 기억한다. → closure
호이스팅은 스코프 단위로 동작
var x = 'hi';
function foo() {
console.log(x);
var x = 'bye'; // var 키워드이므로 undefined 초기화
}
foo(); // undefined
console.log(x) // 'hi'
JavaScript
복사
변수 선언 키워드
let
블록 스코프와 함수 스코프에서 유효한 변수 키워드
•
재할당 ⭕️
•
재선언 
let num = 1;
num = 2;
// error!
// let num = 3;
JavaScript
복사
const
값이 변하지 않는 상수를 정의하며 블록 스코프와 함수 스코프에서 유효한 변수 키워드
선언과 동시해 초기화하지 않으면 문법 에러 발생
값의 재할당이 불가능하므로 const 키워드 사용 시 의도하지 않은 값의 변경을 막을 수 있음
•
재할당 
•
재선언 
const pi = 3.141592;
// error!
// pi = 5;
// const pi = 0;
JavaScript
복사
var
전역 객체 window의 프로퍼티로 블록 스코프를 무시하고 함수 스코프만 유효하다.
•
재할당 ⭕️
•
재선언 ⭕️
var name = 'soyou';
name = 'soyeon';
var name = 'pongduk';
JavaScript
복사
변수 호이스팅에 의해 변수 선언문 이전에 변수를 참조할 수 있음. 값은 undefined를 반환
전역 변수가 window 기능을 덮어씌워서 내장 기능을 사용할 수 없게 만들기도 함.
⇒ var보다는 let 키워드 사용 권장
선언 없이 변수를 사용해도 작동하는데 굳이 선언할 필요가 있나요?
선언 없이 변수를 할당하면 JS 엔진은 선언한 변수를 window의 프로퍼티로 동적 생성하여 전역 변수처럼 작동하게 된다.
이를 암묵적 전역(implicit global)이라고 한다.
변수로 선언된 것이 아니라 전역 객체의 프로퍼티로 추가되었을 뿐이므로 호이스팅 문제는 발생하지 않으며 delete 연산자로 삭제가 가능하다.
밑에서 알아볼 전역 변수의 문제점이 동일하게 나타난다는 뜻이므로 변수를 선언할 때는 꼭 선언 키워드를 사용해 변수를 할당하자!
window 객체
브라우저에만 존재하는 객체로 브라우저의 창을 의미하는 객체지만 이와 별개로 전역 영역을 담고 있음
var로 선언된 전역 변수 및 전역 함수는 window 객체에 속함
함수 선언식으로 함수를 선언하거나 var로 전역 변수를 만들면 window 객체에서도 동일한 값을 찾을 수 있음
전역 변수를 많이 사용하면?
암묵적 결합, 네임스페이스 오염
애플리케이션 내에는 우리가 작성하지 않은 수많은 다른 함수와 로직이 포함된다.
만약 전역 변수의 변수 이름이 중복되면 다른 함수나 로직으로 인해 값이 변경되는 side effect가 발생하게 된다. 어디서든 변수를 참조하고 할당할 수 있음.
전역 변수를 최소화 할수록 side effect를 줄일 수 있다.
전역변수 var 키워드는 의도치 않은 재할당에 의한 상태변화로 코드를 예측하기 어렵게 만드므로 사용을 지양해야함
긴 생명주기
전역 변수의 생명 주기는 애플리케이션의 생명 주기와 동일. 애플리케이션이 종료되지 않으면 계속 메모리 공간을 점유하고 있게 된다.
스코프 체인 상에서 종점에 존재
전역변수의 검색 속도가 가장 느림
변수의 스코프는 좁을수록 좋다! 따라서 전역 변수의 사용을 최소화 해야한다.
전역변수 사용 최소화
전역변수 사용을 최소화하는 방법이 두 가지 있다.
전역변수 객체 사용
하나는 전역변수 객체를 만들어 사용하는 것이다.
var MYAPP = {};
MYAPP.cat = {
name: 'munji',
gender: 'female'
};
console.log(MYAPP.cat.name); // munji
JavaScript
복사
즉시실행함수 사용
즉시실행함수(Immediately-Invoked Function Expression)를 사용하면 생성 후 바로 사용되어 전역에서 사라지게된다.
(function () {
var MYAPP = {};
MYAPP.cat = {
name: 'munji',
gender: 'female'
};
console.log(MYAPP.cat.name); // munji
}());
console.log(MYAPP.cat.name); // error
JavaScript
복사
모듈 패턴
클래스를 모방해 관련이 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만듦
→ 클로저
Strict Mode 엄격 모드
브라우저가 보다 엄격하게 작동하도록 만들어주는 모드
선언 없는 변수 할당의 경우도 strict mode에서는 에러로 판단해주어 실수를 막을 수 있다.
'use strict';
HTML
복사
in Python (Writting)
LEGB Rule
파이썬 변수의 scope 룰을 LEGB 룰이라고 함.
파이썬은 변수나 함수의 정의를 찾을 때,
Local > Enclosed > Global > Built-in의 순서대로 값을 찾음
•
local - 가장 가까운 함수 안 범위
•
Enclosed - 가장 가까운 함수가 아닌 그 다음으로 가까운 함수 범위를 가리킴
•
Global - 함수 바깥의 변수나 import된 Module
•
Built-in - 가장 광범위한 scope. 파이썬 안에 내장되어 있는 함수 또는 속성들로 따로 선언이 없어도 모든 파이썬 파일에서 유효한 범위를 가지고 있다.
>>> a = 5 # Global
>>> b = 10 # Global
>>> def outer():
... a = 10 # outer함수의 local이며, inner함수의 Enclosed
... def inner():
... c=30 # inner 함수의 local
... print(a, b, c)
... inner()
... a = 22 # outer함수의 local이며, inner함수의 Enclosed
... inner()
...
>>> outer()
10 10 30
22 10 30
Python
복사
•
locals() : 로컬 변수 확인
•
globals() : 글로벌 변수 확인
>> a = 1
>> def foo():
b = 3
>> foo()
a in global(): True
b in global(): False
Python
복사
Reference
모던 자바스크립트 Deep Dive, 이웅모, 위키북스 - p.189 ~