쫌만알자! (18) - 스코프

Yeony (Nayeon Kim) · 2022-11-30

앞서 함수를 알아봤습니다. 함수는 {} 이렇게 작성되는 코드블록을 가지고 있습니다. 사실 함수 뿐 아니라 이전에 살펴본 제어문도 코드블록을 가지고 있죠.
코드블록을 언급하는 이유는, 여기서 알아볼 스코프(scope)가 범위를 뜻하기 때문입니다.

스코프란?

스코프는 유효범위입니다. 지금껏 봐왔던 제어문과 함수의 코드블록 내에서 선언된 변수나 혹은 매개변수는 해당 제어문/함수의 코드블록 바깥에서는 접근하지 못합니다.

function add(x, y) { return x + y; } add(1, 2); console.log(x); // ReferenceError: x is not defined

함수에 전달된 매개변수는 함수 body 내부에서만 유효범위를 가지기 때문입니다. 그 안에서만 유효하기 때문에 바깥에서는 아무리 참조하려고 해도 가져올 수 없는 것이죠.

간단한 예제를 살펴봅시다.

let a = 'a in global'; function foo() { let a = 'a in function'; console.log(a); // 'a in function' } foo(); console.log(a); // 'a in global'

똑같은 a로 변수를 선언했지만 함수 내에서는 함수 내의 a가, 외부의 console.log() 에서는 전역변수로 선언된 a가 출력됩니다.

자바스크립트 엔진은 똑같은 이름의 변수가 2개 이상 있으면, 어떤 변수를 참조해야 할 것인지를 결정합니다. 변수는 식별자이기 때문에, 변수 이름은 중복될 수 없습니다. 따라서 결국 변수는 변수명과 스코프 두 가지 모두를 가지고 서로 다른 변수인지 판별하는 것입니다.

스코프 종류

스코프는 전역(global)과 지역(local)로 나뉩니다. 전역에서 선언된 변수라면 전역 스코프를 가지고, 지역에서 선언된 변수는 지역 스코프를 갖게 됩니다.

간단하게 코드블록 {} 만으로 스코프를 나누어보겠습니다.

let a = 'global 1'; let b = 'global 2'; // local 1 scope { let c = 'local 1'; console.log(a, b, c); // 'global 1', 'global 2', 'local 1' // local 2 scope { let b = 'local 2'; console.log(a, b, c); // // 'global 1', 'local 2', 'local 1' } } console.log(a); // 'global 1' console.log(b); // 'global 2' console.log(c); // ReferenceError: c is not defined

이런 스코프를 지정할 때 ES6 문법인 letconst를 사용하면 별 문제가 없습니다. 하지만 var 키워드를 사용해 변수를 선언하게 되면 함수 레벨 스코프에서만 우리가 원하는대로 작동하는 것을 볼 수 있습니다.

함수 레벨 스코프

var a = 1; { var a = 2; } console.log(a); // 2 if(true) { var a = 3; } console.log(a); // 3 for(var i = 0; i < 5; i++) { var a = i; } console.log(a); // 4 function foo() { var a = 999; console.log(a); // 999 } foo(); // 전역 변수가 변하지 않았음을 확인 가능 console.log(a); // 4

함수 내에서 선언한 변수만 지역변수로 취급되는 것을 확인할 수 있습니다. 반면 constlet 키워드는 블록 레벨 스코프를 지원합니다.

블록 레벨 스코프

const a = 1; { const a = 2; console.log(a) // 2 } console.log(a); // 1 if(true) { const a = 3; console.log(a); // 3 } console.log(a); // 1 for(let i = 0; i < 5; i++) { const a = i; } console.log(a); // 1 function foo() { const a = 999; console.log(a); // 999 } foo(); // 처음 선언한 변수 변하지 않음 console.log(a); // 1

const로만 선언했기 때문에 만약 전역으로 선언한 a가 값이 바뀌었다면 아마 상수를 바꿀 수 없다는 에러가 발생했을 것입니다. 하지만 에러가 발생하지 않고, {} 코드 블록 내에서 선언된 모든 a가 해당 블록 내의 값으로 할당되는 것을 확인할 수 있습니다.

렉시컬 스코프

렉시컬 스코프는 함수를 정의한 곳에 따라 상위 스코프를 결정합니다.

let a = 1; // 전역함수 function foo() { let a = 999; bar(); } // 전역함수 function bar() { console.log(a); // 전역 변수 참조 }

위 예시는 함수를 모두 전역에 정의했습니다. 함수는 자신이 정의된 장소(전역/지역)를 기억해 상위 스코프의 변수를 참조합니다.
따라서 bar 함수가 어디서 호출되든 상관없이, bar전역변수 a를 참조하게 됩니다.

함수 👈 이전 글 보기


쫌만알자
Loading script...
© 2022 Nayeon Yeony Kim