변수는 3가지 단계를 거쳐서 생성됨
1. 선언 단계(Declaration phase) : 변수 객체(Variable Object)에 변수를 등록한다. 이 변수 객체는 스코프가 참조하는 대상이 된다.
2. 초기화 단계(Initialization phase) : 변수 객체(Variable Object)에 등록된 변수를 메모리에 할당한다. 이 단계에서 변수는 undefined로 초기화된다.
3. 할당 단계(Assignment phase) : undefined로 초기화된 변수에 실제값을 할당한다.
var x =1;
var y =1;
var x =100;
var y; // 초기화 문이 없는 변수 선언문은 무시된다.
console.log(x,y) // 100,1
var x =1;
if(true){
var x = 10; //block level scope임.
}
console.log(x) // 10이 출력됨.
console.log(score); //undefined //Reference Error가 아님.
var score = 80; // 변수선언
console.log(score) // 80
// 변수선언은 런타임 이전, 변수 할당은 런타임 이후에 실행됨.
console.log(score); // undefined
score = 80;
var score;
console.log(score); //80이 된다..
let과 var의 가장 큰 차이점 : let은 선언과 초기화단계가 분리되어 진행됨.
그래서 호이스팅이 발생하지 않는것처럼 동작함
선언단계와 초기화단계가 분리되어 진행됨. 런타임 이전에 선언단계가 실행되지만,
변수 선언문에 도달했을때 초기화단계가 실행됨 >> 초기화 단계 이전에 변수에 접근하려고 하면 참조에러가 발생함.
일시적 사각지대 : 스코프 시작~ 초기화 시작지점(변수선언) 까지의 변수를 참조 할 수 없는 구간임.
호이스팅이 되는데, 선언단계와 초기화 단계가 분리되었기 때문에 호이스팅이 발생하지 않는것처럼 동작하는것임.
console.log(foo) // Reference Error
//일시적 사각지대.
let foo;
console.log(foo) // undefined;
foo = 1;
console.log(foo) // 1
let의 장점
변수 중복 선언 금지
let bar = 123;
let bar = 456; // SyntaxError 발생함.
let foo = 1;
{
let foo = 2;
let bar = 3;
}
console.log(foo) // 1;
console.log(bar) // Reference Error
원시 타입 (primitive data type), 객체 타입 (object/reference type)으로 나뉨
두 데이터 타입의 차이점은 메모리의 재할당 방식임.
원시 타입의 값은 변경 불가능한 값(immutable value)이며 pass-by-value(값에 의한 전달) 이다.
즉 불변값이라는 의미는 같은 값이 오직 하나만 존재한다는 뜻임.
재할당시 pass by value 이므로, 메모리가 해제되고 새로운 call stack의 메모리값을 부여받음 => 쓰지않는 주소값은 가비지콜렉터에 의해 해제됨
boolean
null
undefined
number
string
symbol (ES6에서 추가)
객체는 데이터와 그 데이터에 관련한 동작(절차, 방법, 기능)을 모두 포함할 수 있는 개념적 존재이다. 달리 말해, 이름과 값을 가지는 데이터를 의미하는 프로퍼티(property)와 동작을 의미하는 메소드(method)를 포함할 수 있는 독립적 주체이다.
자바스크립트는 객체(object) 기반의 스크립트 언어로서 자바스크립트를 이루고 있는 거의 “모든 것”이 객체이다. 원시 타입(Primitives)을 제외한 나머지 값들(배열, 함수, 정규표현식 등)은 모두 객체이다. 또한 객체는 pass-by-reference(참조에 의한 전달) 방식으로 전달된다. => 즉 원시타입이 아닌것은 모두 객체
재할당시 call stack의 주소값은 변경되지 않음 => pass by reference로 동작하기 때문에 call stack의 주소값을 타고 heap에 값을 직접 변경함.
자바스크립트 엔진은 Memory Heap
과 Call Stack
으로 구성되어 있습니다. 가장 유명한 것이 구글의 V8 Engine입니다. 자바스크립트는 단일 스레드 (sigle thread) 프로그래밍 언어인데, 이 의미는 Call Stack이 하나
라는 이야기입니다. 즉 멀티가 되지 않고, 하나씩 하나씩 처리한다는 의미임
(싱글스레드인 이유 ? => callstack 이 하나라서)
Call Stack이란 ?
1)변수 식별자(이름) 저장,
2) 스코프 체인 및 this 관리
3) 코드 실행 순서 관리 등을 수행.
메모리 Heap
10이라는 값 자체는 원시 타입이므로 콜 스택에 저장된다.
변수 a에는 10이 저장된 콜 스택 메모리의 주소값이 저장된다. 변수 식별자 a 자체는 콜스택 상의 '실행 컨텍스트(Execution Context)의 렉시컬 환경(Lexical Environment)'이라는 곳에 저장된다.
원시타입 변수가 할당되면, 할당된 값은 콜스택에, 그 값을 가르키는 주소값은 변수에 저장됩니다. 만약, a = 20 으로 재할당하게 되면, 콜스택에 주소값을 변수에 저장함.
b = 30을 할당하면, 30을 call stack에 저장, + 주소값을 변수 b에저장. 참조되지 않는 데이터는 적절한 시점에 가비지컬렉터에 의해 해제됨.
실제로는 callstack에 변수또한 메모리에 올라가야함.
변수영역과 데이터영역을 분리하는 것의 장점?
- 500개의 변수에 똑같은 숫자 5를 할당하는 상황을 생각해 보자. 각각의 변수마다 데이터를 할당하려면 숫자 데이터가 8바이트를 필요로 하기 때문에 (500*8)바이트를 써야 한다.
- 그 대신 5를 별도의 공간에 한 번 저장하고 해당 주소만 가지고 있는다면, 주소 공간의 크기가 1이라고 가정했을 때 (500 + 8)바이트만 사용해도 된다. 이처럼 변수 영역과 데이터 영역을 분리하면 중복된 데이터에 대한 처리 효율이 높아진다.
let b = 5;
let c = b;
c = 7;
이런식으로 데이터영역 재사용 가능함, 재할당시 새로운 데이터영역(callstack) 에 값이 할당되고, 식별자는 그 주소값을 들고있음
동일한 데이터가 있고, 이를 할당한다면 ? => 메모리는 데이터를 재사용하고, 변수에 데이터주소값을 넣음
배열, 객체, 함수 등은 참조 타입이므로 메모리 힙에 저장된다.
참조타입 데이터가 저장된 메모리 힙의 주소값은 콜스택에 각각 저장된다.
메모리힙의 주소 값이 저장된 콜 스택의 주소값은 각각 변수 b, c, d에 저장된다.마찬가지로, 변수 식별자 b, c, d 이름 자체는 콜스택 상의 '실행 컨텍스트(Execution Context)의 렉시컬 환경(Lexical Environment)'에 저장된다.
보통 callstack의 주소를 참조해서 heap에 접근해 직접 데이터를 변경.
변수의 고유 식별자를 생성합니다.(“myArray”)
콜 스택의 메모리에 주소를 할당합니다.(런타임에 할당될 것입니다.)
힙에 할당된 메모리 주소를 콜스택의 값(value)으로 저장합니다.(런타임에 할당될 것입니다.)
힙의 메모리 주소에 할당된 값(빈 배열 [])을 저장합니다.
obj라는 변수는 callstack에 주소값을 가리킨다. => callstack의 주소값에는 heap에 대한 참조를 가르킨다.
heap에서 동적으로 메모리할당이 일어나는데, 원시값이라면 이를 다시 callstack의 데이터 영역의 값을 가르키는 구조이고,
프로퍼티가 다시 참조 type이라면 heap에서 재할당이 일어나는 방식이다.
여기서 3이라는 원시값은 앞에서 callstack에 할당이 일어났으므로 프로퍼티를 선언할때도 재사용하는 방식이다.
- “변경”이란, 메모리 주소의 변경을 의미합니다.
- let 은 메모리 주소를 변경을 허락합니다.
- const 는 메모리 주소를 변경할 수 없습니다.
ref)