[딥다이브] 스코프

Dongs·2023년 3월 20일
0

스코프?

  • 스코프는 변수의 유효범위이다.
  • 예를 들어,
function add(){
	//,,,
}

if(true){
	//...
} else {
 	//... 
}

for(let i=0; i<10; i++){
 //... 
}

위와 같이 함수선언문, if, else문, for문 등을 사용할 때 항상 {, } 로 묶어와주었었다. 저 중괄호로 묶인 것을 스코프라고 부르고 변수의 유효범위를 나타내는 것이다.

=> 정리하자면 모든 식별자는 자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. 이를 스코프 라고 하며, 즉 스코프는 식별자가 유효한 범위를 말한다!

식별자 결정

var a = 1;

function afk(){
	var a = 10;
  	console.log(a);
};

afk(); //10
console.log(a); //1

위의 코드는 동일한 변수명이 각각 다른 값을 할당받고 있다. 스코프라는 개념이 없었다면 이 코드는 분명 에러가 났을 것이다.

자바스크립트 엔진은 이름이 같은 두 개의 변수 중에서 어떤 변수를 참조해야할 것인지 문맥에 따라 결정하는데 이것을 식별자 결정 이라고 한다! 코드의 문맥은 렉시컬 환경으로 이루어진다!

중복 선언

function afk() {
	var a = 1;
  	var a = 3;
  	console.log(a);
}

afk();  // 3
  • 위의 코드를 실행하면 어떻게 될까? 일반적으로는 에러나 나는게 정상이다. 똑같은 이름의 변수를 선언하였기 때문이다. 그러나 자바스크립트 엔진에서는 var로 중복선언한 변수는 재할당으로 인식한다. 그래서 var로 선언한 변수는 같은 스코프내에서 중복 선언이 가능하다.
function afk() {
	let a = 1;
  	let a = 3; //SyntaxError: Identifier 'a' has already been declared
}

afk();
  • 위의 코드는 변수를 let으로 선언하였고, 실행시키면 'a' has already declared. 이미 선언된 변수입니다. 라는 에러를 출력하며 실행이 되지 않는다. 이로써 알 수 있었던 점은 var는 같은 스코프 내에 중복선언이 되는 반면 let과 const를 사용하면 같은 스코프내에서 같은 변수 명으로 중복선언이 불가능하다! 서로 다른 스코프에서 같은 변수 네임으로 선언하는 것은 가능하다!

전역스코프, 지역스코프

  • 블록스코프 안에 있는 것을 보통 지역스코프, 가장 바깥 범위에 해당하는 스코프를 전역스코프라고 한다!

스코프체인

다음과 같은 코드가 있다.

var x = 1;
var y = 2;

function add(){
	var z = 10;
  function add2(){
	console.log(x+y+z);	
    }
  add2();
}
add(); //13

맨 바깥의 전역스코프에서 add 함수를 호출하고 있고 add 함수 내에는 변수 z만이 존재하며 또 함수 내에서 add2 함수를 호출하고 있다. 결과는
13 이 나온다. 왜일까? x와 y는 함수 내에서 선언되어있지 않은데 어떻게 참조를 한 것일까?

=> 자바스크립트에서는 참조할 변수가 해당 스코프 내에 없으면 상위스코프를 탐색한다. 그리고 또 없다면 다음의 상위스코프를 탐색한다. 최종적으로 전역스코프까지 탐색하게 되는 것이다. 이렇게 체이닝된 형식으로 변수를 찾아 참조하려고 한다. 이 것을 스코프체인 이라고 한다!

=> 변수를 참조할 때 자바스크립트 엔진은 스코프체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.

함수 레벨 스코프

  • 이게 var를 쓰지 않는 것을 권유하는 이유 중 하나인 개념이다.

    다른 언어에서는 함수뿐만 아니라 if, for ,while 문 등 모든 코드 블록이 지역 스코프를 만든다. 이러한 특징을 블록 레벨 스코프 라고 한다! 하지만 var로 선언된 변수는 오직 함수의 코드 블록만을 지역 스코프로 인정하는데 이것을 함수 레벨 스코프 라고 부른다!

var x = 10;

if(true){
 var x=20; 
}
console.log(x); //20

위 코드의 결과는 20이다. 처음에 전역변수로 선언해주고 또 지역스코프 안에서 동일한 변수명으로 20을 선언해주었다. 일반적이라면 변수명 x로 선언된 것은 실질적으로 서로 다른 지역스코프 내에서 선언되었기 때문에 따로 움직여야 한다. 그러나 위의 코드는 일반 블록레벨스코프인 지역스코프를 무시하고 재할당이 되어버렸다. 이 것이 var 가 가지는 특징 중 하나인 함수 레벨 스코프 특성 때문이다. 그와 반대로 let과 const는 이 전에 말했다시피 블록 레벨 스코프를 따른다!

느낀 점

좋은 말할 때 var 쓰지 말자

렉시컬 스코프

var x=1;

function afk(){
 var x=10;
  hmm();
}

function hmm(){
 console.log(x); 
}
afk();
console.log(x);
  • 위 코드를 실행하면 어떻게 될까? 렉시컬 스코프(정적 스코프) 라는 개념을 몰랐을 때는 함수 afk를 호출했을 때는 10이 나오고 마지막 console.log(x) 부분에서는 1이 나올거라고 예상을 했다. 왜냐?

=> afk 함수를 호출했을 때 변수 x는 10으로 할당이 되어있고 그 상태에서 함수 hmm을 호출하여 console에 x를 찍었기 때문에 당연히 지역스코프의 영향을 받아 x = 10 이라고 나올 것이라 생각했다.

  • 하지만 내가 생각한 개념은 동적 스코프(dynamic scope) 일 때의 개념이다.
    동적 스코프는 함수를 어디서 호출했냐에 따라 상위스코프를 결정한다.

    동적스코프를 채택하고 있는 언어가 궁금해서 알아보았다!

    1. Perl
    2. PHP
    3. Common Lisp
    4. Emacs Lisp
    5. Bash 스크립트

    들어본 언어가 PHP 뿐이다..

  • 자바스크립트는 기본적으로 렉시컬스코프(정적스코프) 를 채택하고 있으며 위의 코드는 정적스코프의 관점에서 바라봐야한다. 정적스코프는 함수를 어디서 호출했냐가 아니라 어디서 정의했는지에 따라 상위스코프를 결정한다. 따라서 둘 다 결과가 전역스코프내의 변수 x의 값 1이 출력되는 것이다!

    정적스코프를 채택하고 있는 언어도 궁금해서 알아보았다!

    1. C
    2. C++
    3. JAVA
    4. Python
    5. Ruby
    

회고

오늘도 열심히 ...

profile
자대고 css 하는 프론트엔드 개발자

0개의 댓글