본문 바로가기

Programming

[JavaScript] 스코프 - 함수/변수를 선언할 때 주의해야 되는 4가지

 

코드스테이츠 프리코스에서 자바스크립트 핵심 개념 - 스코프 강의에서 나온 내용들을 다시 정리해보았다.

강의를 통해 var를 이용한 선언과 전역 범위에서 이루어지는 선언이 왜 위험한지 이해하고 넘어갈 수 있었다. 

 

스코프(Scope)란?

변수는 어떠한 환경 내에서만 사용 가능하며, 프로그래밍 언어는 각각의 변수 접근 규칙을 갖고 있는데 변수와 그 값이, 어디서부터 어디까지 유효한지를 판단하는 범위가 바로 스코프이다. 다시 말해, 수 접근 규칙에 따른 유효 범위를 스코프라 칭한다. JavaScript에서는 기본적으로 함수가 선언되는(lexical) 동시에 자신만의 Scope를 가진다.  

 

Rule 1:  Local Scope vs. Global Scope

함수 안에 함수를 넣을 수 있으므로 스코프는 중첩이 가능하며, 스코프에는 전역 스코프(global scope)지역 스코프(local scope) 두가지 종류가 존재한다. 

 

전역 스코프(global scope)는 최상단의 스코프로 어디서든 접근이 가능하다. 변수가 함수 바깥이나 중괄호{} 바깥에 선언되었다면, 전역 스코프에 정의되므로 코드 모든 곳에서 해당 변수를 사용할 수 있다.

그 때문에 두 개 이상의 변수의 이름이 충돌하는 경우가 생길 수 있고 const나 let을 사용하여 선언헀다면, 이름에 충돌이 발생할 때마다 에러가 발생하므로 변수를 선언할 땐 전역 변수가 아닌 지역 변수로써 변수를 선언하는 것이 좋다. 

 

지역 스코프(local scope)는 말그대로 해당 지역에서만 접근할 수 있고 지역을 벗어난 곳에선 접근할 수 없는 범위를 말한다. 지역 변수는 함수 내에서 전역 변수보다 더 높은 우선순위를 가진다. 

let greeting = 'Hello';
function greetSomeone() {
	let firstName = 'Josh';
    return greeting + ' ' + firstName;
}

greetSomeone(); // -> 'Hello Josh' 안쪽 scope에서 바깥 변수/함수를 접근하는 것은 가능
firstName; // -> ReferenceError 바깥쪽 scope에서 안쪽 변수/함수를 접근하는 것은 불가능

 

Rule 2:  Function Scope vs. Block Scope

펑션 스코프(Function Scope)는 함수의 코드 블록만을 스코프로 인정하며 전연 함수 외부에서 생성한 변수는 모두 전역 변수이다. for 문의 변수 선언문에서 선언한 변수를 for 문의 코드 블록 외부에서 참조할 수 있다. var키워드는 함수 레벨 스코프를 따른다. var를 사용하여 선언했다면 var는 블록 레벨 스코프를 따르지 않으므로 블록 밖에서도 접근이 가능하다. 

 

블럭 스코프(Block Scope)는 중괄호로 시작하고, 끝나는 단위를 말하며 모든 코드 블록(함수, if 문, for 문, while 문, try/catch 문 등) 내에서 선언된 변수는 코드 블록 내에서만 유효하며 코드 블록 외부에서는 참조할 수 없다.  

ES6는 블록 레벨 스코프를 따르는 변수를 선언하기 위해 letconst 키워드를 제공한다. const는 값이 변하지 않는 변수, 즉 상수를 정의할 때 사용하는 키워드이다. const로 정의된 값을 재정의하려고 하면 TypeError를 낸다.

//블럭스코프에서 var로 변수가 선언되었을 때
for (var i = 0; i < 5; i++) {
	console.log(i); // -> 0,1,2,3,4 다섯번 iteration함.
} 

console.log('final i: ', i); // -> 'final i: 5' 블럭범위를 벗어나도 접근할 수 있음.

//블럭스코프에서 let으로 변수가 선언되었을 때
for (let i = 0; i < 5; i++) {
	console.log(i); // -> 0,1,2,3,4 다섯번 iteration함.
} 

console.log('final i: ', i); // -> 블럭범위를 벗어나는 즉시 변수를 사용할 수 없으므로 ReferenceError가 뜸 
  let const var
유효 범위 Block Scope Block Scope Function Scope
값 재정의 가능 불가능 가능
재선언 불가능 불가능 가능

위의 테이블을 통해 var를 이용한 선언을 지양해야된다는 것을 알 수 있다. var를 이용하여 변수를 선언했다면, 두 번째 변수가 첫 번째 변수를 덮어쓰게 되고 그에 대한 아무런 에러를 내지 않는다. 이렇게 되면 디버깅이 아주 어려워진다. 

 

Rule 3:  전역 변수와 window 객체

전역 범위를 대표하는 객체가 window이며 window 객체는 모든 객체가 소속된 객체이고, 전역 객체이면서, 창이나 프레임을 의미한다. 

Global Scope에서 선언된 함수, 그리고 var 키워드를 이용해 선언된 변수는 window 객체와 연결된다. 전역 범위에 너무 많은 변수를 선언하지 않도록 주의해야 한다.

var myName = 'Paul';
console.log(window.myName); // Paul -> var를 이용해 선언된 변수는 window 객체와 연결

function foo() {
	console.log('bar');
}

console.log(foo === widow.foo); // true -> global scope에서 선언된 함수는 window 객체와 연결

 

Rule 4: 선언 없이 초기화된 전역 변수

절대로, 선언 키워드 (var, let, const) 없이 변수를 초기화하지 마라! 이런 실수를 방지하고 싶을 경우, Strict Mode를 사용하면 좋다.

function showAge () {
    age = 90; //키워드를 이용하지 않고 선언하였으므로 age는 전역 변수로 취급됨.
    console.log(age)
}
showAge(); // 90
console.log(age); // 90

//Strict Mode를 적용했을 경우

'use strict' // -> 문법적으로 실수할 수 있는 부분들을 에러로 판단함.
function showAge () {
	age = 90; // 여기서 에러가 발생함.
    console.log(age)
}
showAge(); // 90
console.log(age); // 90