DATA TYPE에 대한 이해
코어자바스크립트를 읽고..
데이터 타입에는 기본적으로 2가지가있다.
기본형(원시형) : Boolean, Number, String, null, undefined, symbol
참조형 : 객체, 배열, 함수, 날짜, 정규표현식등
데이터를 복제를 할때 가장 큰 차이점은 기본형은 값이 담긴 주솟값을 복제하는 반면 참조형은 값이 담긴 주솟값들로 이루어진 묶음을 가리키는 주솟값을 복제한다.
정확하지만 이해하기 어렵게 적어놨다. 그래서 천천히 데이터의 선언과 할당이 어떻게 이루어지는지 파악할 필요가 있다.
평소 데이터를 선언할때 바로 값을 할당하곤한다.
let a = 10
하지만 실제 데이터가 선언되고 할당되는 순서는 아래와 같다.
let a;
a = 10
이렇게 봤을때 우선 a
라는 변수를 선언함과 동시에 메모리 어느한곳에(쉽게 메모리주소라고 칭함) 저장을 한다.
예를들어 메모리주소 1번 - a
이런식으로.
그리고 값을 할당을 하게되면 값을 저장하는 한 주소(쉽게 데이터주소라고 칭함) 데이터주소 1번 - 값 : 10
를 저장한다.
이를 합치면
메모리주소 1번 - 이름: a, 값: 데이터주소 1
이렇게 저장이 된다.
즉, 내가 값을 변경하면 실제 값이 변경되는게 아닌 값에 저장되어있는 데이터주소 값이 변경되는것이다.
let a = 10
a = 20
이런상황이라면
메모리주소 1번 - 이름: a, 값: 데이터주소 1
가 있고
데이터주소 1 - 값: 10
이었다가 20을 a 에 할당함과 동시에 20이라는 값이 데이터주소에 있는지 확인하고 없으면
데이터 주소 2 - 값: 20
을 만들고
메모리주소 1번 - 이름: a, 값: 데이터주소 2
로 저장을 다시한다.
불변값은 이름에서도 알수있다시피 값을 변경할 수 없다. 기본형 데이터는 모두 불변값이다. 그렇다고해서 불변값 = 상수
❌다.
값이 변하지 않는다 라는 뜻을 깊게 이해할 필요가 있다. 이전에 변수에 값을 재할당 했을때를 돌이켜보면 메모리의 값이 바뀌는게 아닌 값을 저장해둔 주소를 바꾼다.
let a = 10
a = 20
이렇게 바꿨을때 10이 저장된 주소에 20으로 바꾸는게 아닌 20을 새로운 메모리 주소에 저장을 하고 그 a 의 값 10이 저장되어있는 주소를 20이 저장되어있는 주소로 바꿔준다. 결국 원래있던 10은 없어지지 않는다. 10을 다시 20으로 바꾸는것도 아니다. 이를 불변값이라고 한다.
상수는 재선언, 재할당이 불가능하다. 그래서 불변값이라고 착각 할 수있는것이다.
const a = 10;
a = 20 (불가능)
참조형데이터는 가변값이다. 참조형데이터 할당에 대해서 설명을하면 내부 프로퍼티를 저장하고 그 값의 주소를 또 저장한다.
let obj = {
name: "Jacob"
}
이 상황에서
메모리주소1 - 이름: obj, 값: 데이터주소1
이라는 메모리 주소가 하나 생기고 그 안에 값들을 저장한다.
그리고 변수를 저장할 메모리 주소를 만든다. (메모리 이름은 변수주소 라고 칭한다)
변수주소 1 - 이름: name, 값: 'Jacob'
를 만들고
데이터주소1 - 값: 변수주소1
이렇게 변수주소를 데이터주소에 값으로 첨부한다.
이런상황에서 만약 내가 이름을 바꾼다면?
let obj = {
name: "Jacob"
}
obj.name = "Jun"
이렇게 바꿔도
메모리주소1 - 이름: obj, 값: 데이터주소1
는 바뀌지 않는다. 데이터주소1 안에 Jacob
값을 가지고 있는 주소가 바뀌는 것이다. 즉, 불변값처럼 새로운 값, 객체가 생성되는게 아닌, 내부 프로퍼티의 값만 바뀐것이다. 그래서 가변값이라고 한다.
데이터에는 기본형과 참조형이 있다.
기본형 = 불변값 => 값을 재할당할때 새로운 값을 만들어서 재할당한다.
참조형 = 가변값 => 값을 재할당할때 객체를 새로 만들지 않고 내부 프로퍼티만 값이 변한다.
let a = 10
let b = a
a = 20
console.log(a === b) // false
let obj1 = {
name : "Jacob"
}
let obj2 = obj1
obj2.name = "Jun"
console.log(obj1 === obj2) // true
console.log(obj1.name === obj2.name) // true
이 두가지가 다른 이유는 기본형은 불변값이기 때문에 참조형은 가변값이기 때문이다.
기본형은 복사 후 재할당을 했을때 새로운 값으로 만들어지기 때문에 두개는 전혀 다른값이 되는것이다.
하지만 참조형은 애초에 주소값을 서로 공유를 했기때문에 하나의 obj2 의 값들이 담겨있는 내부 주소값만 바뀌는것일뿐 객체는 그대로이다.
참조형을 불변값으로 사용하기 위해서는 immutable.js 등 라이브러리를 사용하면 가능하다.
참조형을 불변하게 사용하고 싶으면 아예 새로운 객체를 return해주면 된다.
let user = {
name : "Jacob"
}
const changeName = (user) => {
return {
name: user.name
}
}
let user2 = changeName(user)
console.log(user === user2) //false
객체안에 중첩해서 값을 할당했을 경우 복사할때 두가지 방법이 존재한다.
위와 같은 방법은 얕은 복사다.
만약 객체의 내부 프로퍼티가 중첩되어 사용된다면 ?
let user = {
name : "Jacob",
url : {
youtube : "https...."
}
}
const changeName = (user, newName) => {
return {
name : newName,
url: user.url
}
}
let user2 = changeName(user, "Malon")
user2.name = "haha"
user2.url.youtube = ""
console.log(user.name === user2.name) //false
console.log(user.url.youtube === user2.url.youtube) //true
얕은 복사로는 중첩된거는 복사를 못한다.
let copyObj = (target) =>{
let result ={}
if(typeof target === 'object' && target !== ''){
for (let prop in target){
result[prop] = copyObj(target[prop])
}
}else{
result = target
}
return result
}
이렇게 하거나 JSON을 이용해서 하는 방법도 있다.