javaScript/jsDeepDive

선언문에서 let var const를 빼고 선언한다면?

부엉이사장 2023. 5. 25. 03:25

# 일반적인 선언문

let value = "바보";
console.log(value);

일반적인 선언문은 이렇게 생겼다.

그런데 자바스크립트는 ㅈ같이도 아래와 같은 코드도 동작한다.

 

 

 

# let var const없이 선언한 변수

value = "바보";
console.log(value);

잘불러온다..ㅡㅡ

이걸 use strict와 같이쓰면 에러뿜어준다.

될수있으면 선언문을 쓸때 let const를 써서 변수선언을 하자.

파이썬에서 온 사람들은 이거 짜증날거다 ㅠ

근데 이건 변수선언이 아니다!

 

 

 

# 이러한 경우에서 호이스팅은?

console.log(value);
value = "바보";

없는 value를 참조하려했다고 에러뿜어준다.

이 코드가 변수선언으로 동작할때는 호이스팅이 된다고 볼수 없다.(아래에 더씀)

애초에 변수선언문으로 볼수 없다. 정확히는 자바스크립트 엔진에 의해 전역객체에 메모리에 저장이 된거다.

 

 

# 그럼 이 코드는 도대체 뭘까?

let value = "멍충이";
value = "바보";

이제 위코드는 쉽게 이해할 수 있을것이다. value = '바보'는 정확히 재할당문이다.

우리는 선언이없는 변수의 재할당문은 이렇게 메모리에 변수처럼? 저장될수도 있다는걸 알 수 있다.

정확히는 전역객체에 프로퍼티가 생기는거다.

 

 

# 코드의 동작순서.

value = "바보";

위와같은 코드는 실행이될때 먼저 value라는 변수가 있는지 부터 스코프체인으로 탐색한다.

최상단 스코프까지 올라갔을때도 value를 선언한 선언문이 없다면 전역객체에 새로운 프로퍼티 키 벨류로 value : '바보'를 만들어준다고 봐야겠다.

이를 실험하는 코드가 있다.

 

function fuc() {
  value = "바보";
}

fuc();
console.log(value); //value값 잘 참조해준다.

함수내에서 선언문을 저따구로 써봤다.

함수내에서 선언한 변수는 함수스코프내에서만 유효하다.

function fuc() {
  let value = "바보";
}

fuc();
console.log(value); //얘는 참조에러 뿜어줍니다.

예시코드다. 함수내에서 value가 선언 할당되었지만 전역에서 참조하려면 참조에러 띄운다.

함수내에서 선언한 변수는 함수스코프내에서만 유효하다는 뜻이다.

 

때문에 우리는 이렇게 생각할 수 있다.

 

1. 먼저 재할당문으로 인식하여 현재 스코프에서 변수 value를 찾는다.

2. 없으면 상위스코프로 이동하면서 변수 value를 찾는다.(스코프체인)

3. 최종적으로 없으면 전역객체에서 프로퍼티 value : '바보'를 만들어준다.

 

 

# 이 재할당문은 변수선언이 아니라 변수호이스팅은 동작하지 않는다.

참조해줘(); //참조에러 뿜뿜
let없이변수선언할당해줘();

function let없이변수선언할당해줘() {
  value = "바보얌";
}

function 참조해줘() {
  console.log(value);
}

호이스팅이란건 런타임이전에 변수선언문에서 변수의 메모리공간을 먼저 undefined로 채워놓는걸 뜻한다.

하지만 저 value = '바보얌'이란 코드는 런타임내에서 실행되고 스코프체인을통해 식별자를 찾지못하면 최후에 전역객체에 프로퍼티를 만들어준다고 봐야한다. 때문에 함수는 호이스팅이 된것이지만 value='바보얌'이라는 코드를 선언문처럼 동작이 되었다 하더라도 호이스팅이 안된다. 런타임 이후에 일어났기 때문이다. 애초에 변수선언문이라고도 볼수 없다. 그냥 재할당문이다. 다만 런타임에 스코프체인에서 못찾으면 전역객체에 프로퍼티를만들어준다 정도로 이해하면된다.

아래파트에선 편의로 저 재할당문이 변서선언할당으로 동작했다고 써보겠다.

 

 

 

# 이딴걸 왜 포스팅했니?

사실 난 호이스팅에 대해서 공부하면서 함수호이스팅과 변수호이스팅 어떤게 더 먼저 일어날까를 코드로 쳐보고 있었는데.. 코드로 쳐봤던건 이거다.

fuc(); //멍청이로 참조해줌

var value = "바보";

function fuc() {
  value = "멍청이";
  console.log(value);
}

저 함수fuc은 value값을 멍청이로 재할당하고 콘솔로그로 참조하는 코드다.

함수호이스팅으로 함수객체가 메모리에 저장이되고 var로 선언한 변수는 undefined로 초기화된다.

하지만 저 함수몸체에있는 value='멍청이'라는 코드가 만약 선언할당까지 모두 동작하는 경우에는

이 코드로 실험해서 나온결과를 정확히 변수호이스팅이 함수호이스팅된 fuc의 호출보다 먼저일어났다고 볼 수 없다.

그럼 저 안에 value = '멍청이'라는 코드는 재할당문으로 동작했을까 아니면 선언할당문으로 동작했을까?

 

이걸 알 수 있는 방법은 단순하다

전역변수 value를 var로 선언하는게 아니라 let이나 const로 써보면 된다.

 

fuc(); //참조에러 뿜뿜~

let value = "바보";

function fuc() {
  value = "멍청이";
}

위 코드에서 fuc의 함수객체는 런타임 이전에 생성된다.

그리고 함수호출을 할때 value를 멍청이로 재할당하고 콘솔로 찍어주는데 만약 함수fuc내의 코드가 전역의 value변수선언 없이 동작한다고 한다면 단순히 스코프체인을 끝내고 전역변수에 value를 만들어줄것이다.

하지만 결과적으로 참조초기화에러를 내뿜는다. 이 에러는 let이나 const로 선언한 변수를 선언이전에 참조하려할때 뿜는 에러다. 때문에 이 함수를 호출했을때 value에 참조를 하려고했고 이미 식별자 value에 메모리공간이 연결되어있다는 것이다.

 

이는 변수호이스팅이 호이스팅된 함수호출보다 먼저 일어났고 호이스팅된 fuc을 호출했을때 value = '멍청이'라는 코드로  전역변수 value에 접근하려하니 let 변수선언 이전에 접근하려고해서 초기화에러가 뜨는것이다.

중요한건 함수호이스팅으로 함수객체 자체는 런타임이전에 메모리에 할당되고 결론적으로 호이스팅이 되지만 함수 호출은 런타임에 실행된다는 점이다. 함수호출과 함수호이스팅은 전혀 다른 개념이다. 

떄문에 변수호이스팅이 먼저일어났냐 함수호이스팅이 먼저 일어났냐는 결과적으로 저 코드로 알수는 없다.

 

암튼 만약 fuc몸체안에 value재할당문이 선언할당으로 동작했다면 변수선언을 두번하려고해서 신택스에러가 떴을것이다.

let value = "멍청이";
let value = "바보";

 

 

즉 런타임 이전 메모리에 함수객체가 변수선언보다 먼저 생성되고 안되고는 모르겠지만, 호이스팅된 함수호출을 하려고할때보다 변수가 먼저 호이스팅된거라고 볼 수 있다.

 

console.log(value); // undefined
fuc(); // undefined부터 뜨고 멍청이 뜸
console.log(value); // 멍청이

var value = "바보";
console.log(value); // 바보

function fuc() {
  console.log(value); //undefiend
  value = "멍청이";
  console.log(value); //멍청이
}

위 코드처럼 value='멍청이'라는 코드가 재할당문으로 동작한다는걸 확인하였으니 저 안에 있는 value라는 식별자는 호이스팅된 함수가 먼저 호출이 되었더라도 전역변수 value를 참조한다고 볼 수 있다.

위 코드에서 식별자 value가 연결된 메모리공간들에는

undefined - 멍청이 - 바보 순으로 값이 재할당된것을 알 수 있다.

동작 순서는 변수호이스팅/함수객체호이스팅 -  변수 재할당 으로 동작했다고 보면된다.

즉 위 코드는 변수호이스팅에서 TDZ에 함수호이스팅된 함수호출로 접근했다고 볼수도 있을것같다.

 

 


# 이제 이 코드도 이해되겠지?

console.log(value); //undefiend
value = "바보";
console.log(value); //바보
var value = "멍충이";
console.log(value); //멍충이

이코드에서 코드의 동작순서는 뭘까?

var value = '멍충이'라는 코드로 변수호이스팅이 발생해서 value는 undefined로 초기화된다.

그다음 런타임에 value = '바보'라는 코드 재할당문으로 동작이되어 먼저 현재스코프에서 value를 찾는다. value는 호이스팅되어 undefiend가 할당되어있다. 그리고 재할당문이 동작이되어 value에는 '바보'가 값으로 할당이되고

그이후 var value = '멍충이'라는 코드가 동작되어 value에는 '멍충이'가 값으로 재할당된다.

ㅅㄱ

 

 

# 저게 변수선언할당이 아니라는 증거 하나 더

var value = "바보";
value_test = "멍청이";

console.log(globalThis.value);
console.log(globalThis.value_test);

delete value;
delete value_test;

console.log(globalThis.value);
console.log(globalThis.value_test); //value_test : '멍청이' 프로퍼티가 삭제되었당

전역객체에 var로 선언한 변수는 프로퍼티로 등록이 된다.

처음 콘솔에서 둘다 나오지만 프로퍼티를 delete로 삭제하고나서는 찐 변수일경우 동작하지 않는다

저 코드에서 value, value_test를 삭제하였지만 변수선언문으로 쓴 value는 삭제가 안되고 재할당문으로 선언(?)한 value_test는 삭제되었다.

위 환경은 브라우저 환경에서만 동작한다. nodejs환경에선 둘다 지워주드라.

 

 

 

# 이건 실행컨텍스트를 배워야 알 수 있는것같아.

사실 함수객체가 메모리에 호이스팅되서 들어가면서 함수몸체내에 변수들을 어떻게 관리하는지 아직은 감으로만 찍어본수준이다. 실행컨텍스트를 배우면 더 자세하게 알 수 있을것같다.

'javaScript > jsDeepDive' 카테고리의 다른 글

전역변수사용을 억제해야하는 이유?  (0) 2023.05.31
스코프에 관해서  (0) 2023.05.26
함수 호이스팅  (0) 2023.05.23
함수의 진짜 이름  (0) 2023.05.19
함수 리터럴에 대해서  (0) 2023.05.16