그동안 수업 들으면서 정리했던 부분들을 매일 작성했어야 했는데 이해하는 데 시간이 좀 걸리고 잠깐 블로그를 놓았던 것 같다
기본적으로 변수는
선언 + 식별 + 타입 + 값 + 스코프
로 이루어져 있다
선언은 var,let,const
등이 있고 식별자는 변수의 이름이라 생각하면 좋고 타입또한 js에선 실행 당시 알게 되지만 그렇다고 없어야 하는 건 아니다.
타입 스크립트를 배울 때나 다른 언어에서는 외부적으로 작성해줘야 하니 기억해놓자
그리고 값과 스코프가 있다
ex) var name = "lee" , let age = 15, const birtday = 0101
식별자는 말 그대로 변수를 식별하기 위해 필요한 것으로 우리가 코드를 작성할 때 변수 선언을 하게 될 경우 선언문 뒤에오는 것이 보통 식별자이다
var name = "lee"
에서 name
이 식별자 역할을 한다
이때 식별자의 규칙이 하나 있는데
a
,b
이런식으로 적을 경우 혼란을 야기하니 주의하자.JS에선 타입이 2가지가 있는데
타입을 중요시 생각하는 이유는 값이 어떤 메모리에 들어가는지를 생각한다면 이해하기 쉽다
Primitive / Value Types 들은 immutable 하다.
여기서 immutabel이란 불변성으로 변하지 않는 것이다
일단 이 타입들은 값이 보통 stack에 존재하게 되는데
쉽게 표현하자면
STACK이란 메모리에 &10 번지 메모리 주소에 식별자 a
로 값이 1
저장된것이다
우리가 코드를 조금 배웠다면 만약 이 a
가 var나 let
로 선언됐다면 다시 변경할 수 있다고 생각할 수 있다.
그럼 여기서 의문점이 생긴다. 값을 변경하는데 "그럼 &10번지에 있던 a
의 값이 변경되는 것이기 때문에 immutable한 건 아니지 않나요?" 라고 생각할 수 있다
하지만 메모리 저장 과정을 본다면 immutable 한거구나 이해할 수 있다
새롭게 값을 할당했을 때 &10번지에 있는 a
값이 변경되는 것이 아니라 실제론 &10번지에 있던 a
가 아닌 새로운 &11번지에 a
를 새로운 값으로 저장한다.
그리고 원래 &10번지에 a
는 아무도 참조하고 있지 않는 상태가 되어 G.C가 돌게되어 삭제된다.
그렇기에 &10번지의 값을 변경한 것이 아니기 때문에 불변하다는 법칙이 적용되는 것이다
종류로는
Refernce 타입은 말 그대로 참조 타입으로 메모리에 저장될 때 값이 저장된다기 보단 메모리 주소가 저장된다 생각하면 이해하기 편하다
저장되는 값들을 생각하면 Object타입일 텐데 이 Object들은 값이 가변성을 띈다
가변성을 띄는 값들은 stack에 저장할 경우 낭비가 심하니 Heap이란 메모리 영역에 저장하는데 이 Heap영역은 매우 넓은 저장공간이다
이곳에 저장된 이 Object의 위치들을 stack에서 메모리 주소로 저장시키는 것이다.
그렇기에 이 메모리 주소를 참조 하고 있다고 해서 참조 즉 Refrence 타입이다.
그럼 우리가 const를 사용하면서 생기는 의문점이 확실하게 이해된다
왜 const 로 Refrence타입을 저장했는데 object나 array안은 변경 되는거지? 이부분이 확실하게 이해된다
const obj = {id:1, name:"lee"}
obj. name = "hong"
console.log(obj) // {id:1 , name:"hong"}
왜 이렇게 되는것일까? 위에 설명한 걸 그대로따라 한다면 위의 obj
는 실제 STACK 메모리 주소에 obj란 식별자에 {id:1,name:"lee'}
가 저장된 것이 아니라 HEAP에 저장된{id:1,name:"lee'}
값의 주소를 가지고 있는 것으로 해당 값이 HEAP의 &100번지에 존재한다면 실제 STACK에는 obj
란 식별자에 &100번지라는 메모리 주소가 저장되어 있는 것이다.
그렇기에 HEAP에 있는 객체의 name
을 변경한다고 해도 &100번지라는 메모리 주소는 동일하니 실제로 obj
의 값은 변하지않은것이다.
따라서 이런것도 생각해 볼수가 있다
const obj1 = {id:1, name:"lee"};
const obj2 = obj1
obj2.id = 3
console.log(obj1) //{id:3, name:"lee"}
console.log(obj2) // {id:3, name:"lee"}
만약 메모리 주소를 이해 못했으면 바로 "????"이 뜰 것 같은 코드다.
근데 위의 Refrence타입의 저장 과정을 이해했다면 이해하기 편하다.
Primitive에서 위와 같이 쓴다면??
let a = 1
let b = a
a = 5
console.log(a) // 5
console.log(b) // 1
다르게 동작하는 것을 확인할 수 있는데 이유는 이렇게 let a = b
라고 사용할 경우 b가 가진 값을 그대로 가져가는 것을 볼 수 있는데 Primitive 타입은 해당 값을 그대로 저장하고 있기 때문에 그대로 값 1
만 전달해주는 것으로 끝나지만
Refrence 타입은 메모리 주소를 그대로 가져가는 것이기 때문에 해당 메모리 주소의 값이 변경된다면 다른 변수에도 영향을 끼치는 것이다
obj1
의 값인 &100번지 메모리 주소를 복사해서 obj2
에 할당했으니 obj2
도 &100번지 메모리 주소를 값으로 가지고 있을테고 obj1
을 통하든 obj2
를 통하든 &100번지의 값을 변경할 경우 두가지 다 결국에 &100번지를 보고 있으니 값이 바뀌는 것이다.
string타입에 대한 STACK과 HEAP저장은 이전에 constant pool에 한번 다룬 적이 있으니 한번 보면 좋을 것 같다
https://velog.io/@lee_moi/javascript-%EC%97%90%EC%84%9C%EC%9D%98-constant-pool
기본적인 타입과 타입이 저장되는 메모리의 설명은 끝났는데 JS를 배우면서 항상 주의해야 하는 것이 있따.
JS는 객체 기반의 언어이며 원시값(primitive)를 제외한 모든 것이 객체다.
(참고로 string 또한 JS에선 primitve값으로 보고 있지만 다른 언어에선 객체로 인정한다. char Array 로 분명히 단어 하나하나가 쭉 array로 나열된 것이기 때문에 [index]
로 접근 할 수 있는것이다)
JS는 런타임에 타입이 정해지는 동적(Duck) 타이핑!(할당 시 추론 infernce)