변수는 유효범위(스코프)와 생명주기를 가진다.
📌 변수의 생명주기
변수는 선언에 의해서 생성되고 할당에 의해서 값을 가진다.
식별자(변수) 는 값이 저장된 메모리 주소를 식별한다.
만약 변수에 생명주기가 없다면 프로그램이 종료되기 까지 계속 메모리 공간을 사용하게 되고 이 변수가 사용되지 않는다면 메모리 공간을 낭비하게 된다.
변수는 자신이 선언된 위치(스코프)에서 생성되고 자신이 선언된 위치에서 소멸된다.
1️⃣ 지역변수
var sentence = "this is global variable";
function foo(){
var x = 10;
console.log(sentence); // ?
var sentence = "this is global variable";
return x;
}
foo();
console.log(x) // ReferenceError
1. 변수 x
함수 내부에서 선언된 지역변수는 함수가 값을 반환하고 종료되면 함수와 함께 소멸된다
지역변수 x 는 함수가 호출되면 엔진에 의해서 undefined 로 먼저 초기화 된다 (함수가 호출 되어야 선언됨을 기억하자!)
그리고 변수 할당문을 만나면 값이 할당 된다
함수가 return 문을 통해서 x 를 반환해줌과 동시에 x 는 소멸되고 메모리상에서 사라진다
즉, 지역변수는 함수가 호출되어 실행되는 동안에만 유효하다
📌 호이스팅은 스코프 단위로 동작한다
변수 선언이 스코프의 가장 선두로 끌어올려진 것처럼 동작하는 것을 말함!
전역변수 : 전역 스코프의 가장 선두로 끌어올려짐
지역변수 : 지역 스코프의 가장 선두로 끌어올려짐
2. console.log(sentence)
foo 가 호출되면 foo 함수 생성에 의해 만들어지는 지역스코프의 가장 선두로 sentence 의 선언이 올라가고 undefined 로 초기화 된다
그리고, 변수를 출력하려고 할때 렉시컬 환경을 참조해서 스코프 체인을 사용해 변수를 검색한다
우선 자기 자신의 스코프 부터 검색하고 변수 sentence 가 자신의 스코프에 있기 때문에 foo 함수 내부의 지역변수 sentence 를 참조할 수 있고 값이 할당되기 전 단계 이기 때문에 undefined 를 출력한다.
2️⃣ 전역변수
var x = 'global';
function foo(){
var x = 'local';
console.log(x);
return x;
}
foo();
console.log(x);
함수나 객체 밖에서 선언한 전역변수는 호출이나 참조 없이 바로 해석되고 실행된다
foo 함수 내부에서 return 문을 통해 지역변수 x 를 반환하고 메모리상에서 소멸 됐지만,
전역변수로 사용한 x 는 함수의 생명주기와 상관없이 프로그램이 끝날때까지 메모리상에서 공간을 차지하게 된다
브라우저 환경에서 전역객체는 window 객체이고, 전역변수는 window 객체의 프로퍼티가 된다.
전역변수의 생명주기는 전역객체의 생명주기와 일치한다!
🧐 전역변수의 문제점
- 전역변수로 선언한 변수는 위치 상관없이 모든 코드에서 참조가 가능하다
이말은, 모든 코드가 전역변수의 값이나 상태를 변경할 수 있다는 것을 의미하고 의도치 않게 상태가 변경될수도 있다는 것이다
변수의 유효범위는 좁을수록 좋다
- 긴 생명주기
전역변수의 생명주기는 전역객체의 생명주기와 같고 이는 프로그램이 끝날때 까지 메모리 상에서 공간을 차지한다는 것이다
긴 생명주기는 전역변수의 변경될수 있는 가능성을 높인다, 전역변수의 상태가 변경될 수 있는 기회가 많다는 것!
- 스코프 체인의 종점
스코프 체인 상에서 가장 상위에 위치하기 때문에 검색 속도가 가장 느리다
- 네임스페이스 문제
자바스크립트는 파일이 분리되어 있어도, 전역스코프는 하나이다.
다른 파일에서 같은 이름의 변수를 사용하면 변수의 재할당이 발생한다.
📌 클로저 활용해서 전역변수 사용 줄이기
클로저 사용시 장점
- 지역변수의 사용을 줄임
- 캡슐화
아래의 2가지 경우를 비교해보자
function outer(){
var myName = "woong";
console.log(myName);
}
outer(); // woong
console.log(myName) // referenceError
function outer(){
var myName = "woong";
console.log(myName);
return {
inner(){
var greeting = "hello"
console.log(`${greeting} ! ${myName}`);
}
}
}
var foo = outer(); // woong
foo.inner(); // hello ! woong
console.log(foo.myName()); // undefined;
outer 함수는 객체를 반환해주고 그 객체에는 inner 메서드가 있다.
inner 메서드를 정의할 때 inner 메서드의 상위스코프인 outer 함수의 지역변수 myName 을 사용했다
outer 함수를 실행하면 객체를 반환해주고 그 객체에는 inner 메서드가 있다
지역변수의 생명주기를 생각해보면 outer 함수가 호출되고 종료되었기 때문에 myName 지역변수가 메모리상에서 사라졌을 것이다.
하지만, inner 메서드를 실행하면 myName 이 정상적으로 출력된다.
🧐 Why?
클로저는 함수가 정의 되는 시점의 렉시컬 환경을 기억한다
inner 함수가 정의된 시점에 지역변수 myName 을 참조했기 때문에 그 환경을 기억한다
따라서 함수 밖에서 outer 함수가 반환해준 inner 함수를 호출할 때마다 언제든지 myName 변수에 접근할수 있게 되고, 전역변수 처럼 사용할 수 있다.
하지만 이 myName 에 직접 접근할수 없어 상태를 변경할 수 없기 때문에 안전하다는 장점이 있다.
지금은 렉시컬 환경, 실행 컨텍스트에 대한 이해가 완벽하지 않아서 글을 적는 와중에도 잘 모르겠다,,
2개를 공부하고 나서 다시 클로져 정리를 해야겠다
'javascript > DeepDive' 카테고리의 다른 글
💡 클로져 (0) | 2023.02.01 |
---|---|
[Js 딥다이브] 15. let, const (0) | 2022.11.14 |
[Js 딥다이브] 13. 스코프 (0) | 2022.11.07 |
[Js 딥다이브] 12. 함수 (2) (0) | 2022.11.06 |
[Js 딥다이브] 12. 함수 (1) (1) | 2022.11.05 |