let a = 3;
let b = a;
b = 5;
console.log(a === b); // false
let object = { aa: 1, bb: "bb", cc: "1c" };
let object2 = object;
object2.cc = "c";
console.log(object === object2); // true
똑같이 변수 하나를 선언하고(여기서는 a, object) 다른 변수(b, object2)에 복사를 했다. 같은 방법을 사용했기 때문에 같은 결과가 나올것 같지만 다른 결과가 나왔다. 그 이유는 데이터 타입에 따른 메모리에 할당하는 법이 다르기 때문이다.
데이터 타입의 종류
기본형 : Number, String, Boolean, null, undefined, Symbol
참조형 : Array, Function, Date, RegExp, Map, WeakMap, Set, WeakSet
우선 기본형에 대해서 살펴보자
let a = 1;
변수 a
에 숫자형 1을 할당하는 간단한 코드이다. 이 코드는 메모리 영역을 변화시킨다.
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a , 값 : @5002 | |||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | 1 |
주소에 바로 값을 할당하는 것이 아닌 주솟값을 할당하고 주솟값의 위치에서 데이터를 입력한다.
이렇게 하는 이유는 데이터를 효율적으로 관리할 수 있기 때문이다.
공간을 미리 확보하고 데이터를 선언하면 나중에 데이터를 변환할때 공간을 다시 바꿔야하는 경우가 생길 수 있기 때문에 연산 비용이 들어간다.
let a = 1;//1단계
let b = a;//2단계
b = 3//3단계
console.log(a===b)//false a = 1, b = 3
이제 단계별로 메모리를 살펴보자
let a = 1;//1단계
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a , 값 : @5002 | |||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | 1 |
맨위에 표와 동일하다.
let b = a;//2단계
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a , 값 : @5002 | 이름 : b, 값 : @5002 | ||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | 1 |
여기서는 변수 b
에 변수 a
와 같이 @5002
의 주솟값을 할당한다.
b = 3//3단계
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: a , 값 : @5002 | 이름 : b, 값 : @5003 | ||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | 1 | 3 |
이렇게 새로운 주소에 3
을 할당하고 그 주솟값을 b
에 할당한다.
a
와b
에 주솟값이 다르기 때문에a!==b
이다.
이제 참조형에 대해서 알아보자
let object = { a : 1, b : 2, c : 3 };
변수 object
에 객체를 할당한다.
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: object , 값 : @5002 | |||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | @7002~? | 1 | 2 | 3 |
주소 | 7002 | 7003 | 7004 | 7005 |
데이터 | 이름 : a, 값 : @5003 | 이름 : b, 값 : @5004 | 이름 : c, 값 : @5005 |
기본형과 똑같이 참조형도 변수에 주솟값이 담긴다. 하지만 기본형과 다르게 직접적으로 담긴 주소(@5002)에 가보면 다른 주소의 영역(@7002~?)이 담겨져있고 7002, 7003, 7004으로 가보면 1,2,3 값들이 담겨있다.
let object = { a : 1, b : 2, c : 3 };//1단계
let object2 = object;//2단계
object2.c = 10//3단계
console.log(object === object2)//true
//object === { a : 1, b : 2, c : 10 }
//object2 === { a : 1, b : 2, c : 10 }
이상하게 분명 object2.c
만 바꿨는데 object.c
도 같이 변해있다. 메모리가 어떻게 동작하는지 보자
let object = { a : 1, b : 2, c : 3 };//1단계
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: object , 값 : @5002 | |||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | @7002~? | 1 | 2 | 3 |
주소 | 7002 | 7003 | 7004 | 7005 |
데이터 | 이름 : a, 값 : @5003 | 이름 : b, 값 : @5004 | 이름 : c, 값 : @5005 |
위에 선언할때 표랑 똑같다.
let object2 = object;//2단계
메모리 | ||||
---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 |
데이터 | 이름: object , 값 : @5002 | 이름 : object2, 값 : @5002 | ||
주소 | 5002 | 5003 | 5004 | 5005 |
데이터 | @7002~? | 1 | 2 | 3 |
주소 | 7002 | 7003 | 7004 | 7005 |
데이터 | 이름 : a, 값 : @5003 | 이름 : b, 값 : @5004 | 이름 : c, 값 : @5005 |
여기서 object
와object2
가 같은 주솟값
을 갖고 있다. 같은 주솟값
을 갖고 있다는 것이 중요하다.
object2.c = 10//3단계
메모리 | |||||
---|---|---|---|---|---|
주소 | 1002 | 1003 | 1004 | 1005 | 1006 |
데이터 | 이름: object , 값 : @5002 | 이름 : object2, 값 : @5002 | |||
주소 | 5002 | 5003 | 5004 | 5005 | 5006 |
데이터 | @7002~? | 1 | 2 | 3 | 10 |
주소 | 7002 | 7003 | 7004 | 7005 | 7006 |
데이터 | 이름 : a, 값 : @5003 | 이름 : b, 값 : @5004 | 이름 : c, 값 : @5006 |
10
을 @5006
에 할당했다. 여기서 봐야할 것이 object2.c = 10
로 값을 변경했지만 object
와 object2
는 똑같은 주솟값을 갖고 있다. 그래서
console.log(object === object2)//true
//object === { a : 1, b : 2, c : 10 }
//object2 === { a : 1, b : 2, c : 10 }
이런 결과가 나온다.
참조형을 복사하는데 원본에 영향을 안미치게 해보자
여러 방법이 있지만 개인적으로 이방법이 편한것 같다.
let object = { a : 1, b : 2, c : 3 };
let object2 = {...object}
object2.c = 10
console.log(object===object2)//false
//object === { a : 1, b : 2, c : 3 }
//object === { a : 1, b : 2, c : 10 }
이 방법은 얕은 복사
라고 한다.
얕은 복사
에는 문제가 있다. 바로 참조형안에 참조형이 있으면 복사값이 원본값에 영향을 미친다.
let deepObj = { a : 1, b : 2, c : { A : "A", B : "B" } };
let deepObj2 = {...deepObj};
deepObj2.c.B = "KK"
console.log(deepObj)//{ a : 1, b : 2, c : { A : "A", B : "KK" } }
console.log(deepObj2)//{ a : 1, b : 2, c : { A : "A", B : "KK" } }
깊은 복사
는 참조형안에 참조형이 들어가 있는 형태여도 복사값이 원본값에 영향을 안미친다.
function copyObj(obj) {
const result = {};
for (let key in obj) {
if (typeof obj[key] === "object") {
result[key] = copyObj(obj[key]);
} else {
result[key] = obj[key];
}
}
return result;
}
이런 함수를 하나 만들어서 사용하거나
let deepObj3 = JSON.parse(JSON.stringify(deepObj));
//이 방법은 느리다
이런식으로 하면된다.
let deepObj = { a : 1, b : 2, c : { A : "A", B : "B" } };
let deepObj2 = copyObj(deepObj);
deepObj2.c.B = "KK"
console.log(deepObj)//{ a : 1, b : 2, c : { A : "A", B : "B" } }
console.log(deepObj2)//{ a : 1, b : 2, c : { A : "A", B : "KK" } }
let deepObj3 = JSON.parse(JSON.stringify(deepObj));
deepObj3.c.B = "ABC"
console.log(deepObj)//{ a : 1, b : 2, c : { A : "A", B : "B" } }
console.log(deepObj3)//{ a : 1, b : 2, c : { A : "A", B : "ABC" } }
이 글은 정재남님께서 쓰신 코어자바스크립트 책을 참고 하였습니다.
잘못된부분이나 부족한 부분이 보이신다면 알려주시면 감사하겠습니다.