# 변수의 목숨이 붙어있는 시간
딥다이브에선 이걸 변수의 생명주기라고 하는데.. 그냥 이 변수를 참조할수 있는 시간을 말한다.
//존나
//오래
//걸리는
//코드1
var value = '멍청이';
//존나
//오래
//걸리는
//코드2
이런식으로 전역스코프에 변수 value를 선언하였다
이걸 그림으로 그려보면
자바스크립트는 선언부분을 다 끌어올린다.
그래서 모든 식별자에는 메모리주소가 연결되고 그곳에있는 값으로 참조할 수 있다.
그림을 좀 복잡하게 그렸는데 걍 전역에서 선언한 변수는 코드가 끝날때까지 참조할 수 있다는거다..
사실 그림이 정확하지 않는데 순서대로라면 선언부분 var value부분만 먼저 실행되고 오래걸리는코드1동안 undefined로 참조되고, 그다음 value = '멍청이'재할당문이 실행되서 오래걸리는코드2에선 '멍청이'를 참조할 수 있다.
호이스팅도 살짝 감미해봤다능..
# 함수안에 있는 지역변수는 얼마동안 살아있을까?(함정)
//존나
//오래
//걸리는
//코드1
function fuc() {
let value = "바보";
}
//존나
//오래
//걸리는
//코드2
이번에 함수안에서 지역변수 value를 선언해보았다.
그럼 이걸 메모리구조상에서 표현하면 어떻게 될까?
짜잔~ 함정이다 ㅋㅋ
저 코드는 함수선언문으로 함수만 정의해놓은거니까 그렇다.
함수정의는 식별자에 연결된 메모리주소에 연결된 메모리주소에 함수객체가 들어간다.
함수객체가 들어가는것이지 지역변수선언은 이루어지지 않는다. 지역변수 value가 메모리자체에 없다는거다.
때문에 이전 스코프에서 렉시컬스코프 공부할때 함수정의때 식별자 스코프체인이 정의시점에서 이뤄지는건가 하고 착각했었다.
그렇다면 지역변수 value = '바보' 는 언제 메모리에 생겨날까?
# 함수 호출시점에 지역변수는 얼마나 살아있을까?(찐~)
//존나
//오래
//걸리는
//코드1
function fuc() {
var value = "바보";
}
//존나
//오래
//걸리는
//코드2
fuc()
조렇게 함수를 호출을 해봤다~
그림으로 그리면
요렇게 된다 ㅎㅎ 저 연두색시간동안만 지역변수 value가 참조될수있다.
함수 호출후 함수몸체의 코드가 끝나면(return까지) 지역변수 value의 메모리공간이 해제되어 가용메모리풀에 반환된다고 함.
참고로 함수의 지역스코프도 사라진단다~
# 그럼 무조껀 지역변수는 전역변수보다 생명이 짧겠네?
console.time("전역변수 value 멍청이는 얼마동안 살아있을까?");
let value = "멍청이";
function fuc() {
console.time("지역변수 value 바보는 얼마동안 살아있을까?");
let value = "바보";
console.timeEnd("지역변수 value 바보는 얼마동안 살아있을까?");
}
fuc();
console.timeEnd("전역변수 value 멍청이는 얼마동안 살아있을까?");
타이머메서드로 시간을 재봤다.
뭐 코드 실행할때마다 시간이 다르긴한데.. 그래서 총 세번을 코드를 실행시켰다.
출력결과를 가져와보면,
무조껀 지역변수의 생명주기는 전역변수의 생명주기보다 짧다.
물론 저 타이머가 정확히 변수선언시점에 재지는건 아닐테지만..
참고로 함수내에 console.log(value)를 하면 지역변수 타이머가 더 길게 되지만 무조껀 지역변수를 잰 타이머는
전역변수 타이머보다 작다.
console.time("전역변수 value 멍청이는 얼마동안 살아있을까?");
let value = "멍청이";
function fuc() {
console.time("지역변수 value 바보는 얼마동안 살아있을까?");
let value = "바보";
console.log();
console.log();
console.log();
console.log();
console.log();
console.timeEnd("지역변수 value 바보는 얼마동안 살아있을까?");
}
fuc();
console.timeEnd("전역변수 value 멍청이는 얼마동안 살아있을까?");
이렇게 중간에 콘솔로그를 겁나쳐줘도
무조껀 전역변수의 생명주기가 더 길다.
# 함수를 여러번 썼을때는?
console.time("전역변수 value 멍청이는 얼마동안 살아있을까?");
let value = "멍청이";
function fuc() {
console.time("지역변수 value 바보는 얼마동안 살아있을까?");
let value = "바보";
console.log();
console.timeEnd("지역변수 value 바보는 얼마동안 살아있을까?");
}
fuc();
console.log("다음");
fuc();
console.log("다음");
fuc();
console.timeEnd("전역변수 value 멍청이는 얼마동안 살아있을까?");
요로케 함수호출을 여러번 했다.
함수가 실행되고 종료되는동안이 전역변수의 생명주기라고 배웠다.
근데 결과는?
첫번째 함수호출때는 4ms정도되는데 그이후로 호출한 함수에서 지역변수는 0.2ms정도로 확 줄었다.
그이유는 사실 나도 몰라서 질문하긴했는데.. 인터프리터언어라서 한줄씩 코드가 해석되고 실행되기때문에 처음 함수몸체내 코드를 해석할때 오래걸리는거라고 한다. 뭐 사실 정확히는 모르겠지만 그렇단다.
v8엔진이 메모리에 변수공간을 만들어버리는것만 아니라면..
나중에 더 알수 있게되겠지?
# 전역변수란?
위에서 설명했듯이 전역에서 선언한 변수다.
- var로 선언한 변수
var dog = "무찌";
console.log(globalThis.dog); //브라우저 환경에선 무찌뜸. 노드js에선 undefined
전역에서 선언한 변수는 모두 전역객체의 프로퍼티가 된다.
변수뿐아니라 표현식으로 정의한 함수, 객체 등등도 모두 프로퍼티로 저장이된다.
우리가 엄청 자주쓰는 console.log도 console이라는 프로퍼티키가 값으로 객체를 참조하고, 이 객체안의 log라는 메서드를 호출하는거다.
- let, const로 선언한 변수
let dog = "무찌";
console.log(globalThis.dog); //브라우저 노드js 둘다 undefined
얘네는 둘다 undefined가 뜨는데 나중에 실행컨텍스트에서 나오는데 아직 잘 이해는 안된다.
전역환경레코드의 선언적환경레코드에 생겨난다고 한다. 그래서 전역객체의 프로퍼티로는 없는거란다..
뭔소린지아직은 이해가 안된다. 암튼 전역객체에는 없고 어딘가에 있어서 전역에서 참조할수 있다고 한다.
+ 선언 없이 쓴 재할당문?
dog = "무찌";
console.log(globalThis.dog); //브라우저 노드js 둘다 '무찌'를 출력함
이건 이전 포스팅에서 썼다.
재할당문을 썼는데 이 식별자가 선언이 안되어있으면 스코프체인후 최종적으로 전역변수처럼? 동작한다.
https://jacobowl.tistory.com/175
선언문에서 let var const를 빼고 선언한다면?
# 일반적인 선언문 let value = "바보"; console.log(value); 일반적인 선언문은 이렇게 생겼다. 그런데 자바스크립트는 ㅈ같이도 아래와 같은 코드도 동작한다. # let var const없이 선언한 변수 value = "바보";
jacobowl.tistory.com
이 포스팅에 썼다.
var dog1 = "무찌";
dog2 = "도리"; //얘네 둘다 전역객체의 프로퍼티로 들어간다.
delete globalThis.dog1;
delete globalThis.dog2; //지워봤다
console.log(globalThis.dog1); //윈도우에선 dog1이 삭제안됐다.
console.log(globalThis.dog2);
전역객체에서 프로퍼티를 삭제해도 선언문으로 선언한 변수 dog1은 지워지지 않는다.
근데 선언없이 재할당문만 쓴 dog2는 전역변수처럼 동작하지만 delete로 전역객체 프로퍼티에서 지워진다.
이건 변수가 아니다~~
++
전역객체를 가르키는 식별자는 각각 브라우저는 window, nodejs는 global이다.
ES11부터 globalThis로 통일해서 참조할수 있다. 때문에 globalThis는 브라우저 환경 꾸진데선 동작안할수도 있다.
# 전역변수사용을 억제합시다
전역변수사용을 억제하자는 이유는 아래와 같다.
- 전역변수 생명주기는 조온나 길다
위에서 야매로 실험해봤듯이 전역변수의 생명주기는 매우매우 길다. 전역변수가 많은만큼 그만큼 메모리를 많이, 오래 차지하고 있는것이다.
- 암묵적 결합 implicit coupling
이건 사실 let이나 const로 선언하면 테스트할때 다 에러떠서 실수할일이 이제 거의 없긴한데..
var로 선언할때는 문제가 있단다.
var dog = '무찌';
// 코드 100만줄~~~
// 코드 100만줄~~~
// 코드 100만줄~~~
// 코드 100만줄~~~
var dog = '도리' //난 변수 dog을 선언했던걸 까먹었음..ㄷㄷ
뭐 이런식으로 코드가 조올라 길다고 하자면..
이때 변수 dog을 선언했던걸 까먹어서 밑에다가 더 쓰면, 아래 쓴 선언문은 재할당문으로 동작할거고, dog라는 식별자를 참조할때 var dog = '도리' 이전 참조는 모두 무찌, 그 이후로는 '도리'를 갖고올거다.. 뭐 암튼 짧은코드론 코드가 이상하게 동작하면 바로 찾아서 알겠지만 몇만줄 이렇게 되고 나중에 함수내에서 저 스트링을 어쩌고 저쩌고 하는데 타입이다르거나 그러면 에러 팡팡파티
# 스코프체인상 종점존재
let dog = "무찌";
function fuc1() {
let dog = "도리";
console.time("#1 지역스코프를 참조한경우는?");
console.log(dog);
console.timeEnd("#1 지역스코프를 참조한경우는?");
}
function fuc2() {
console.time("#2 스코프체인이 일어난경우는?");
console.log(dog);
console.timeEnd("#2 스코프체인이 일어난경우는?");
}
fuc1(); //얘는 인터프리터 땜에 오래걸린다고 생각하고 생략..
fuc2(); //얘는 인터프리터 땜에 오래걸린다고 생각하고 생략..
fuc1();
fuc2();
이건 뭐.. 제대로 안나온다.
제목그대로 스코프체인 검색이 오래걸린다는건데 저 콘솔타이머로는 제대로 못잰다.
노드js환경에선 지역스코프가 더 오래걸린다고 뜨는 경우가 많고
브라우저환경에선 스코프체인이 더 오래걸린경우가 많다.
암튼 뒤죽박죽이다. 아래사진은 브라우저 환경에서 코드 돌려본거다.
멍개님 답으로는
전역변수 dog 무찌는 메모리구조에서 데이터영역에 쌓이고, 지역변수 dog도리는 스택영역에 쌓인다고 한다.
함수는 호출시점에 스택영역에 스택프레임?이 생성된다고 한다. 스택영역에서 스택영역에 접근하는것과 데이터영역에 접근하는 차이가 있어서 그렇다고 한다.
뭐 사실 잘 모르겠다.
# 네임스페이스오염
이름 참 어렵게 지어놨다.
대충 브라우저에서 한번 실험해보자.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="index.js"></script>
<script src="index_test.js"></script>
</body>
</html>
이건 html이다. 그리고 이 페이지를 index.js와 index_test.js라는 두 js파일이랑 스크립트 태그로 연결했다.
//index.js
var dog = '도리';
//index_test.js
var dog = '무찌';
두 파일에 각각 똑같은 식별자로 변수선언을 했다. 대신 할당한 스트링은 각각 다르다.
서버키고 콘솔에서 저 dog을 참조하면 어떻게 될까?
무찌를 가져온다.
그럼 index_test.js에서 var dog = '무찌' 변수선언할당문문을 지우고 dog식별자를 참조해보면?
이젠 도리를 가져온다.
자바스크립트 브라우저 환경에선 이렇게 파일이 분리되어 각각 다른 독립적인 파일이지만, 하나의 전역스코프로 공유해버린다는거다. 한 js파일에서 저렇게 선언 두번을 한거면 대충 cntl+f로 알아내겠지만, 이렇게 파일이 여러개로 분할된경우는 어디서 선언된 변수를 썼는지 찾기 굉장히 어려울거다.
- let으로 선언해보면?
//index.js
let dog = '도리';
//index_test.js
let dog = '무찌';
요렇게 let으로 선언으로 바꿔봤다.
이 에러를 보고 아 내가 선언을 두번 했구나 ㅋㅋ 에러를 찾아보자~하고 index_test.js로 와서 들여다보는데..?
난 분명 dog라는 식별자에 선언 한번만 똑바로 해놨다? 시발 뭐야? 어디서 에러생기는거야?
이렇게 되는거다. 또한 브라우저에서 뭐 예를들어 부트스트랩이나 폰트같은 라이브러리를 스크립트로 가져왔고 여기서 쓰는 식별자랑 중복되는걸 쓰게된다면.. 엄청 뒤죽박죽되겠지? 물론 이런 라이브러리들은 따로 처리를 해놨겠지만 말이다.
'javaScript > jsDeepDive' 카테고리의 다른 글
객체를 커스텀해보자. 프로퍼티어트리뷰트 (플래그) (0) | 2023.06.12 |
---|---|
즉시실행함수 (0) | 2023.06.01 |
스코프에 관해서 (0) | 2023.05.26 |
선언문에서 let var const를 빼고 선언한다면? (0) | 2023.05.25 |
함수 호이스팅 (0) | 2023.05.23 |