함수의 매개변수를 비롯한, 모든 변수는 코드의 일정 범위 안에서만 유효하다는 성질을 갖는다.
이렇게, 특정 변수를 선언하고 사용할 수 있는 코드 상의 유효 범위를 스코프(scope)라고 한다.
( 이는 JavaScript의 문법이 아니라 '변수가 어디까지 쓰일 수 있는지'의 범위를 의미한다. )
block이란 중괄호{ }
(curly brace)로 감싸진 것을 말한다.
예시를 보면 바로 감이 온다.
function hi() {
return 'i am block';
} // function의 내부는 하나의 block
for (let i = 0; i < 10; i++) {
count++;
} // for문 내부도 하나의 block
if (i === 1) {
let j = 'one';
console.log(j);
} // if문의 내부도 하나의 block
{ }
(block)내부에서 변수가 정의되면 변수는 오로지 { }
(block)내부에서만 사용할 수 있다.
function add(x, y) { // 변수 `x`와 `y`가 정의됨
return x + y;
}
add(2, 3); // 5
console.log(x); // error
{ }
(block)내부에서 정의된 변수를 local(지역) 변수라고 부른다.
{ }
(block) 으로 감싸진 변수이고, 당연히 { }
(block) 내부에서만 사용할 수 있다.
const color = 'red';
console.log(color); // red
function returnColor() {
console.log(color); // red
return color;
}
console.log(returnColor()); // red
{ }
(block) 밖인 global scope에서 정의된 변수를 global variable(전역변수)라고 한다.
{ }
(block) 외부에서 정의되었기 때문에 당연히 { }
(block) 외부에 접근할 수 있다.
지역 변수와 다른 점은 Global(전역) Scope 는 { }
(block) 내부에도 자유자재로 접근할 수 있다는 것!
위의 예시에서 color
라는 변수는 global 변수이기 때문에 returnColor함수의 block에서도 접근이 가능해서 'red'를 반환한 것이다.
const five = 5;
function add5(x) {
return x + five; // 바깥 스코프의 `five` 변수에 접근
}
add5(3); // 8
⇒ 다시 한번 Global(전역) Scope 예시가 나왔는데, 이처럼 함수 내부 코드에서 무조건 매개변수 혹은 지역 변수만 사용할 수 있는건 아니다.
이렇게 함수는 x = five
를 return 하기 위해 열심히 five
변수를 찾다가....
함수 내부에서 찾을 수 없으면 바깥 스코프에 접근해서 함수 바깥의 변수 five
를 가져와 사용한다.
👉 이런 함수가 여러 겹 중첩(nested)되어 있다면 어떨까?
const five = 5; // 난 global variable! function add5(x) { function add(y) { // 난 y! x가 누구지? 하고 바깥 scope를 봤더니 x는 add5 함수의 매개변수고 인자는 3이네! return x + y; } return add(five); // 난 x! add 함수의 매개변수로 five를 줄거야! 바깥 scope를 봤더니 five는 5네! } add5(3); // 8
위 예시에서 코드의 실행 흐름을 알 수 있다.
위에서부터 순차적으로 진행되다가 x는five
, y는x
라는 모르는 친구를 만난다.
(모르는 친구 = 우리 반 아닌 친구 =현재{ }
내에 없는 변수)
그 모르는 친구를 찾기 위해 x 와 y는 바로 바깥쪽 scope에 접근하고, 바깥쪽 scope에 있으면 사용하고 없으면 그 다음 바깥쪽 scope에 접근해서 찾아보는...
이런 과정을 되풀이한다. 이 과정이 바로 스코프 연쇄(scope chain)!!
그리고 가장 바깥에 있는 scope를 최상위 스코프(top-level scope) 혹은 전역 스코프(global scope)라고 부르고 전역 스코프에서 선언된 변수를 전역 변수(global variable)라고 한다.
위 예시에서five
가 바로 전역 스코프에서 선언된 전역 변수인 것!
전역 변수(global Variable) 에 대해 주의해야할 점이 있다!
전역 변수는 해당프로그램의 어디에서나 사용할 수 있는 global namespace를 갖는다.
변수를 명시적으로 전역 스코프에서 선언하지 않더라도, 한 번도 선언되지 않은 이름으로 안쪽 스코프에서 let
, const
, var
를 붙여주지 않고 변수를 선언하면 전역 변수가 되어버린다.
function func() {
variable = 1; // `variable`이라는 변수가 선언된 적 없으므로, 전역 변수가 된다.
}
func();
console.log(variable); // 1 - scope 내부 변수에는 접근할 수 없어야 하는데 variable가 전역 변수가 되어 버려서 접근 가능
전역 변수는 코드의 어떤 부분에서든 아무런 제한 없이 접근하고 조작할 수 있다.
마치 WeWork 1층 한복판에 누구나 제약 없이 쓸 수 있는 맥북을 갖다 놓는것.....
let stars = 'North Star';
const callMyNightSky = () => {
stars = 'Sirius'; // stars 변수의 값을 바꿔 버림
return 'Night Sky: ' + stars;
};
console.log(callMyNightSky()); // Night Sky: Sirius
console.log(stars); // Sirius
혹은 이런 경우도 있다.
(1) callMyNightSky
함수에서 새로운 stars
변수를 선언하고 싶었는데 깜빡하고 let
키워드를 작성하지 않았다...😭
(2) callMyNightSky
을 호출하면 stars
변수에 "Sirius" 가 할당된다.
(3) 전역 변수 였던 stars
가 수정된 것...! 다른 함수에서 전역 변수인 stars
을 사용하면 값이 수정된 "Sirius" 으로 사용하게 된다.
(4) 전역 변수는 프로그램이 종료될때까지 계속 살아있는 반면에, 지역 변수는 { }
(block)이 끝나면 더 이상 변수가 살아있지 않고 쓸 수 없게 된다.
🤨 전역 변수가 계속 살아있다면, 다음과 같은 문제점이 생긴다.
A.js
파일을 고쳤는데 아무런 상관도 없어 보이던 B.js
파일의 코드가 오동작하게 될 수도 있다.)좋은 Scoping 습관 😁
위와 같이 전역 변수가 여기저기서 수정되면 안되기 때문에 변수들은 block scope으로 최대한 나눠놔야 한다.
타이트한 scope(tightly scoping)의 변수는 코드의 품질을 UP! 시켜준다.
- 코드가 block으로 명확하게 구분되기 때문에 코드 가독성이 좋다.
- 코드를 단순히 나열된 것이 아니라 각각의 기능별로 block을 나누기 때문에 코드를 이해하기 쉽다.
- 나중에 코드를 수정할 일이 있을 때, 코드를 오랜만에 보더라도 block 별로 잘 나뉘어 있어서 유지보수가 쉽다.
- 프로그램이 끝날때까지 변수가 살아있는 것이 아니라서(block이 끝나면 지역 변수의 삶이 end...) 메모리도 절약된다.
같은 { }
내의 단일 scope에서는 같은 이름을 갖는 서로 다른 변수가 존재할 수 없다.
하지만 스코프 연쇄가 일어나는 특성을 이용하면 같은 이름의 서로 다른 변수를 만들 수 있다.
let currencySymbol = "$";
function showMoney(amount) {
let currencySymbol = "₩"; // currencySymbol 이라는 변수가 다시 정의됨
console.log(currencySymbol + amount);
}
showMoney("500"); // ₩500
console.log(currencySymbol + "1000"); // $1000
⇒ showMoney
함수 내부에서 currencySymbol 변수를 재 정의 한것처럼, 바깥쪽 scope에 존재하는 변수와 같은 이름을 갖는 변수를 안쪽 scope에서 재정의할 수 있다.
그러면 안쪽 scope에서는 바깥쪽 scope에 있는 이름이 무시된다.
이런 현상을 변수 가리기(Variable Shadowing) 하고 한다.
scope는 코드가 작성된 구조에 의해서 결정되는 것이지, 함수 호출의 형태에 의해 결정되는 것이 아니다.
뭔말인고 하니....
function add5(x) {
const five = 5;
return add(x);
}
add5(3); // error (add is not defined)
function add(x) {
return five + x; // error (five is not defined)
}
add
라는 함수가 add5
라는 함수 안에서 호출되었어도, add
함수 내부에서 add5
함수의 스코프 안에 있는 변수에 접근할 수는 없다.
스코프는 코드가 작성된 구조에 의해 결정되는 성질이기 때문에 아래와 같이 작성해야 한다.
function add5(x) {
const five = 5;
function add(x) {
return five + x;
}
return add(x);
}
add5(3); // 8
개발 왕초보 코린이입니다!
이 내용은 혼자 동영상 강의&구글링을 통해 배운 내용을 정리하는 것으로, 제가 이해하고 넘어간 개념이 틀렸거나 더 보충할 개념이 있다면 댓글 남겨주시면 정말 감사하겠습니다!!