javascript

[javascript] var,let,const/변수 scope/호이스팅

잘할수있을거야 2022. 5. 1. 09:29

let/const - es6에 도입


Global Scope vs Local Scope

 

1. Global Scope(전역 스코프)

- 모든 곳에서 참조 가능

 

2. Local Scope(지역 스코프)

- Function Scope, Block Scope 두가지가 존재

 

Local Scope에 해당하는 Function Scope, Block Scope 두스코프를 알아보기 전에 Global Scope에서 var/let/const의 차이와 호이스팅의 개념을 같이 알아보자.

이후에 Local Scope인 Function Scope와 Block Scope를 알아보고 모두 섞인 개념을 알아보자.


전역 스코프에서 선언된 var/let/const 변수

	<script type="text/javascript">
		var test = "var";
		let test2 = "let";
		const test3 = "const";

		console.log(test);
		console.log(test2);
		console.log(test3);
	</script>

var/let/const 변수가 전역 스코프에서 선언되었다.

var/let/const가 지역 스코프가 아닌 전역 스코프에서 선언되면 모든 곳에서 참조 가능하다.


var 변수

재할당이 가능하다.

중복 선언은 무시된다(에러 발생x)

중복 선언+할당의 경우 할당만 적용된다(에러 발생x)

호이스팅시 변수 선언 호이스팅이외에 undefined로 초기화가 동시에 일어난다.

 

재할당 가능

	<script type="text/javascript">
		var test = 1;
		console.log(test); //1
		test = 2; 
		console.log(test); //2
		var test = 3; //선언 무시되고 할당만 됨
		console.log(test); //3
		var test = 4;
		console.log(test); //4
		var test; //선언 무시됨
		console.log(test); //4
	</script>

 

실제로는 코드 실행전 준비 단계에서 var 변수에 맞는 호이스팅이 적용되어 다음과 같이 된다.

	<script type="text/javascript">
		var test = undefined; //var 변수 호이스팅 적용
		test = 1;
		console.log(test);
		test = 2;
		console.log(test);
		test = 3;
		console.log(test);
		test = 4;
		console.log(test);
		console.log(test);
	</script>

변수가 선언된 스코프에 해당하는 가장 윗 부분으로 변수 선언이 올라온다.

var 변수이기 때문에 호이스팅시 변수이외에 추가적으로 undefined로 초기화 된다.


var 변수 호이스팅에 의해 다음과 같은 코드는 문제가 되지 않는다.

	<script type="text/javascript">
		console.log(test);
		var test = 1;
		console.log(test);
	</script>

var 변수 호이스팅 적용

	<script type="text/javascript">
		var test = undefined; //var 변수 호이스팅 적용
		console.log(test);
		test = 1;
		console.log(test);
	</script>

초기화 되지 않은 var 변수를 참조해도 에러가 나지 않는 이유가 호이스팅시 undefined로 초기화 되는 것 때문이다.

	<script type="text/javascript">
		var test;
		console.log(test);
	</script>

 


(추가됨)

선언된 변수가 없을 때 var 생략 후 초기화하면 전역변수로 저장된다.

function test() {
  a = 10; //var 생략시 전역변수로 저장됨
}

test();
console.log(a); //10

 

엄격모드를 사용하면 ReferenceError 발생

"use strict";

function test() {
  a = 10; //ReferenceError
}

test();

 

let 변수

var 변수와 마찬가지로 let 변수또한 재할당이 가능하다. 

그러나 var변수처럼 변수 재선언이 들어가 있는(재선언, 재선언+할당) 문법은 에러가 난다. 

 

재할당 가능

	<script type="text/javascript">
		let test = 1
		console.log(test);
		test = 2;
		console.log(test);
	</script>


var 변수처럼 재선언이 무시되는 것이 아니라 에러가 발생한다.

 


let 변수 또한 값을 할당하지 않으면 undefined로 초기화되지만 var변수와는 호이스팅이 다르게 적용된다.

	<script type="text/javascript">
		let test;
		console.log(test);
	</script>

 

let 변수는 var 변수와는 다르게 호이스팅시 선언만 최상위로 올라가며 undefined 초기화는 실제 변수가 선언된 코드 줄에서 초기화 된다.

	<script type="text/javascript">
		console.log(test);
		let test;
	</script>

호이스팅 적용후 다음과 같이된다.

	<script type="text/javascript">
		//test변수 선언 호이스팅. let변수라 실제 선언이 있는 부분에서 undefined로 초기화된다.
		//console.log(test); //에러 발생
		//test=undefined;
	</script>

 


	<script type="text/javascript">
		/* 
			TDZ(Temporal Dead Zone) - 일시적 사각지대
			test 변수를 사용할 수 없음
		*/
		let test = 100;
	</script>

 

(추가됨)

 

const 

재할당 불가. 선언과 초기화를 동시에 해야 한다.

var변수의 재선언+초기화 같은 문법이 let에는 불가능했던 것 처럼 당연히 const도 불가

const변수 또한 let 변수에 해당하는 호이스팅이 일어난다(TDZ)

 

 

선언과 초기화 분리되어 에러

	<script type="text/javascript">
		const con;
		con = 10;
	</script>

 

재할당 에러

	<script type="text/javascript">
		const con = 10;
		con = 20;
	</script>

 

지금까지 전역 스코프에서 사용된 var/let/const 변수에 대해 알아보았다.

호이스팅은 모든 스코프의 가장 상단에 일어난다. -> Local Scope 또한 가장 위로 호이스팅의 개념이 적용된다.


Local Scope

 

1. Function Scope

함수 내부에서만 변수가 유효하다.

2. Block Scope

블록 내부에서만 변수가 유효하다.

 

전역 스코프가 아닌 경우

var은 Function Scope을 따르고

let/const는 Block Scope를 따른다.


var은 Function Scope

 

현재 스코프에 해당하는 변수가 우선으로 참조된다.

	<script type="text/javascript">
		var test = "global scope var";

		function func() {
			var test = "function scope var";
			console.log(test);
		}

		func(); 
	</script>

 

해당 스코프에 변수가 없으면 Scope Chain으로 외부 스코프에서 순차적으로 찾다 전역 스코프까지 찾다 없으면 에러가 난다.

	<script type="text/javascript">
		var test = "global scope var";

		function func1() {

			var test2 = "func1 scope var";

			function func2() {
				console.log(test);
				console.log(test2);
			}

			func2();
		}

		func1();
	</script>


모든 스코프(전역,로컬Function,로컬Block)에서는 항상 호이스팅이 일어난다. 위의 예제들에서도 함수 스코프에 해당하는 함수 선언의 가장 윗부분에 var 변수의 호이스팅이 일어난다. 

 

func() 함수에 해당하는 함수 스코프에서 test가 선언되어 있지 않은 것 같은 오해를 만들어 외부 스코프인 전역 스코프에서 test를 참조할 것 같지만 함수 스코프 내부에서 호이스팅이 일어나 undefined가 출력된다.

	<script type="text/javascript">
		var test = "global scope var";

		function func() {
			console.log(test); //?
			var test = "function scope var";
		}

		func();
	</script>

 

(호이스팅 적용)

	<script type="text/javascript">
		var test = "global scope var";

		function func() {
			var test = undefined;
			console.log(test); 
			test = "function scope var";
		}

		func();
	</script>

let/const는 Block Scope

	<script type="text/javascript">
		let test = "global scope let";

		{
			let test = "first block scope let";
			console.log(test); //현재 블록에서 test 참조
		}
		
		{
			console.log(test); //외부 스코프인 전역 스코프에서 test 참조
		}


		{
			let test = "third block scope let"
			{
				console.log(test) //현재 블록에 없으니 바로 외부 스코프에서 test 참조
			}
		}
	</script>


함수 내부에 선언된 let/const는 함수 바디의 { } 블록에서 유효하다.

(이 경우에는 블록 스코프의 범위가 함수 바디 전체로 어떻게 보면 함수 스코프의 범위와 동일하다 볼 수 있다)

	<script type="text/javascript">
	
		function func() {
			let l = "l: 함수 전체에서 변수 유효";
			var v = "v: 함수 전체에서 변수 유효";
			
			{
				let l2 = "l2: 이 블록에서만 유효";
				var v2 = "v2: 함수 전체에서 변수 유효"; //함수 스코프를 따르기 때문
			}
			
			console.log(v);
			console.log(v2);
			console.log(l);
			console.log(l2); //에러 
		}

		func();
	</script>


스코프의 범위가 완전히 동일한 경우가 아니라면 각 스코프마다 고유의 식별자가 가능하기 때문에 다음의 코드는 가능하다.

	<script type="text/javascript">
		function func() {
			let l = "함수 let l";
			console.log(l);
			{
				let l = "블록 let l"
				console.log(l);
			}

		}

		func();
	</script>

 

블록 스코프에 해당하는 let 변수와, 함수 스코프에 해당하는 var 변수의 유효범위가 함수 전체 구간으로 완전히 동일하기 때문에 식별자 중복 에러가 난다.

	<script type="text/javascript">
		function func() {
			var l = "함수 var l";
			let l = "함수 let l";

		}
	</script>


전역 스코프 var변수 v에 30을 대입하려 의도 했지만, 블록 스코프의 let변수 v가 블록 최상단으로 호이스팅 되어 일시적 사각지대에서 let 변수 초기화를 진행하여 에러가 난다.

	<script type="text/javascript">
		var v = 10;
		{
			v = 30; //에러
			let v = 20;
		}
	</script>

 

const의 블록 스코프는 let과 동일하다.


블록 스코프에 해당하는 for문, if문

 

함수 스코프를 따르는 var test변수는 현재 선언된 구간이 함수 내부가 아니기 때문에 전역 스코프 유효범위를 가진다.

for문 내부에서 var 변수를 선언하면 오해를 가져와 다른 변수를 덮어쓰는 상황이 발생 할 수 있다.

	<script type="text/javascript">
		var test = {
			name : "kim",
			age : 10
		};

		for (var test = 1; test <= 10; test++) {
			console.log('console');
		}

		console.log(test.name); //원치 않는 상황 발생
	</script>

그러므로 if ( ){ 변수 선언.... } , for(변수 선언...){   } 사용시 상황에 맞게 let, const를 사용하는 것이 바람직하다


for문 바디에 const를 사용해도 문제가 없다.

https://www.inflearn.com/questions/366392

 

for문을 돌면서 const에 재할당 되면 원래 에러가 나야하지 않나요? 그리고 예제에서 let (또는 var)

[사진]   본 강의 중 4:45 부터 나오는 내용입니다. const를 쓰게되면 for 문을 돌면서 product 및 price에 값이 재할당 되게 될텐데요. (본 예제에서는 allProducts내의 아이템이 3개이므로 3번 재할당 됨) co

www.inflearn.com

	<script type="text/javascript">
		for (let i = 1; i <= 10; i++) {
			const con = i;
			console.log(con);
		}
	</script>

 

기본 for문의 초기화 모든 반복 통틀어 한번만 수행되므로 const를 사용할 수 없다.

	<script type="text/javascript">
		for (const i = 1; i <= 10; i++) {
			console.log(i);
		}
	</script>

 

for.. in 은 각 반복에 한번씩 수행되므로 ()에 const를 사용할 수 있다.

	<script type="text/javascript">
		var obj = {
			name : "킴",
			age : 10
		};

		for ( const propertyNameString in obj) {
			const propertyString = propertyNameString;
			console.log("속성(스트링): ", propertyString);
			const propertyValue = obj[propertyString];
			console.log("값: ", propertyValue);
		}
	</script>


함수 선언식의 호이스팅

전역 스코프에서 선언된 함수 선언식은 전역 스코프의 최상단으로 선언된다.

함수 스코프에서 선언된 함수 선언식은 함수 스코프의 최상단으로 선언된다.

관련 예제는 생략하고 학습하면서 가졌던 의문점을 써본다.

 

함수 또한 객체이기때문에 함수명에 해당하는 이름을 가진 var 변수에 담긴다.

	<script type="text/javascript">
		console.log(test);
		function test() {
			console.log('test1');
		};
		function test() {
			console.log('test2');
		}
	</script>

가장 마지막에 선언된 것이 전역 스코프 var test 변수에 담겼다.


함수명과 변수명이 동일한 경우에는 변수에 함수가 먼저 저장된다.

https://stackoverflow.com/questions/40675821/what-happens-when-javascript-variable-name-and-function-name-is-the-same

 

What happens when JavaScript variable name and function name is the same?

I have the following code, where I declare a function and after it, a variable with the same name as the function: function a(x) { return x * 2; } var a; alert(a); I expected this to alert

stackoverflow.com

	<script type="text/javascript">
		console.log(test);
		var test = 10;
		console.log(test);
		test(); //에러

		function test() {
			//함수 코드 본문
		}
	</script>

첫 로그에 undefined가 출력되지 않고 함수 객체가 저장되어 함수 코드가 출력된다.

함수 객체가 들어있는 test변수가 이미 존재하므로 var test=10;은 무시되고 test=10;만 적용된다.

두번째 로그에는 10값이 찍힌다. 이제 test함수는 사용불가하다.

 

이와 같이 함수도 변수에 저장되기 때문에 동일 스코프에서 함수 이름과 동일한 이름을 가진 let,const변수가 존재하면 에러가 난다.

	<script type="text/javascript">
		let test = '테스트 변수';
		function test() {
		}
	</script>