원시 자료형
1, 'string', true, undefined, null, Symbol()
// number, string, boolean, undefined, null, symbol
참조 자료형
[0, 1, 2] // 배열
{name: 'kim', age: 17} // 객체
function sum(x, y) { return x + y } // 함수
원시 자료형의 특징
1. 원시 자료형을 변수에 할당하면 메모리 공간에 값 자체가 저장됩니다.
let score = 90;
변수 score를 선언하면 컴퓨터에서 메모리 공간을 확보하고 score라는 이름을 붙힙니다. 그 다음 score라는 공간에 90을 할당합니다.
→ 값 자체를 공간에 저장
2. 원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달됩니다.
let score = 90;
let copyScore = score; // 90
// 원본을 변경한다면?
score = 80;
console.log(copyScore); // 90
// -> 복사한 변수의 값은 변경되지 않음
3. 원시 자료형은 변경 불가능한 값입니다. → 읽기 전용 값
let score = 90;
score = 80;
처음에 score 변수에 90을 할당한 다음, 80을 할당한다면 값이 변경된 것 같습니다.
하지만 이것은 값이 변경된 것이 아닙니다.
처음에 확보한 메모리 공간에 score라는 이름을 붙이고 그곳에 90을 저장합니다.
그 다음 80을 재할당하는데, 기존에 있던 score에 있는 90을 80으로 바꾸는 것이 아니라, 다른 메모리 공간에 80을 저장하고 그곳의 이름을 score라고 붙이는 것입니다.
그리고 사용하지 않게 된 20은 쓰레기 값이 되어 가비지 콜렉터에서 자동으로 삭제합니다.
참조 자료형의 특징
1. 참조 자료형을 변수에 할당하면 메모리 공간에 주소값이 저장됩니다.
힙(heap)이라고 부르는 특별한 저장 공간에 참조 자료형을 저장한 후, 이 저장 공간의 주소값을 변수의 메모리 공간에 저장합니다.
이 주소값을 통해서 참조 자료형에 접근할 수 있게 됩니다.
2. 참조 값을 갖는 변수를 다른 변수에 할당하면 주소값이 복사되어 전달됩니다.
let arr = [0, 1, 2];
let copyArr = arr; // [0, 1, 2]
// 원본을 변경한다면?
arr.push(3); console.log(copyArr); // [0, 1, 2, 3]
// -> 원본의 주소를 참조하는 것이므로 같이 변경됨
// 같은 주소를 참조하고 있는 것이라 값 자체가 복사되었다고 할 수는 없음
3. 참조 자료형은 변경이 가능한 값입니다.
let arr = [0, 1, 2];
arr.push(3);
console.log(copyArr); // [0, 1, 2, 3]
// 변수가 참조하고 있는 주소에 저장되어 있는 값을 변경
// 저장공간의 주소값을 저장하고 있기 때문에 변경이 가능
원시 자료형의 경우 값의 크기가 거의 일정해서 새로운 공간을 확보한 후 값을 복사하는 방법이 괜찮지만, 크기가 일정하지 않은 참조 자료형은 매번 값을 복사하면 효율성이 떨어지기 때문에 변경이 가능하도록 설계되었습니다.
배열을 복사하는 방법
slice()
let arr = [0, 1, 2];
let copyArr = arr.slice();
console.log(copyArr); // [0, 1, 2]
console.log(arr === copyArr); // false
slice()를 사용해서 만든 배열을 원본 배열과 요소는 같지만 참조하는 주소는 다릅니다. 따라서 복사한 배열에 요소를 추가해도 원본은 변경되지 않습니다.
copyArr.push(3);
console.log(arr); // [0, 1, 2];
console.log(copyArr); // [0, 1, 2, 3];
spread syntax
배열이 할당된 변수명 앞에 …
을 붙여주면 사용이 가능합니다.
let arr = [0, 1, 2];
console.log(...arr); // 0 1 2
let arr2 = [...arr];
console.log(...arr2); // 0 1 2
console.log(arr === arr2) // false
객체를 복사하는 방법
Object.assign()
let obj = { name: 'kim', age: 17 };
let copyObj = Object.assign({}, obj);
console.log(copyObj)
// { name: 'kim', age: 17 }
console.log(obj === copyObj) // false
spread syntax
let obj = { name: 'kim', age: 17 };
let copyObj = {...obj};
console.log(copyObj)
// { name: 'kim', age: 17 }
console.log(obj === copyObj) // false
하지만 참조 자료형 내부에 참조 자료형이 중첩되어 있다면 위의 방법들을 사용해도 참조 자료형 내부에 참조 자료형이 중첩된 구조는 복사가 불가능합니다.
let students = [
{
name: "kim",
grade: 1,
number: 15,
},
{
name: "lee",
grade: 2,
number: 7,
}
]
let copyStudents = students.slice();
console.log(students === copyStudents); // false
console.log(students[0] === copyStudents[0]); // true
// students[0]와 copyStudents[0]는 여전히 같은 주소를 참조하고 있음
slice()
, spread syntax
, Object.assign()
등의 방법들은 한 단계만 복사할 수 있습니다.
이를 얕은 복사
라고 합니다.
깊은 복사
깊은 복사는 참조 자료형 내부에 중첩되어 있는 모든 참조 자료형을 복사하는 것을 말합니다.
하지만 자바스크립트 내부적으로는 깊은 복사를 수행할 수 없고, 다른 문법을 응용하여 깊은 복사와 같은 결과물을 만들어 낼 수는 있습니다.
JSON.stringify(), JSON.parse()
JSON.stringify()
는 참조 자료형을 문자열 형태로 변환하여 반환하고, JSON.parse()
는 문자열의 형태를 객체로 변환하여 반환합니다.
const arr = [0, 1, [2]];
const copyArr = JSON.parse(JSON.stringify(arr));
console.log(arr); // [0, 1, [2]];
console.log(copyArr); // [0, 1, [2]];
console.log(arr === copyArr); // false
console.log(arr[2] === copyArr[2]); // false
하지만 중첩된 참조 자료형 중에 함수가 포함되어 있다면, 위 방법을 사용할 시에 함수가 null로 바뀝니다. 따라서 완전한 깊은 복사라고 볼 수는 없는 방법입니다.
외부 라이브러리
lodash
const lodash = require('lodash');
const arr = [0, 1, [2]];
const copyArr = lodash.clonedeep(arr);
console.log(arr); // [0, 1, [2]];
console.log(copyArr); // [0, 1, [2]];
console.log(arr === copyArr); // false
console.log(arr[2] === copyArr[2]); // false
'JavaScript' 카테고리의 다른 글
[JavaScript] 클래스 (Class) (0) | 2023.03.15 |
---|---|
[JavaScript] 클로저 (Closure) (0) | 2023.03.15 |
[JavaScript] if와 else if에서의 문제 해결 (0) | 2023.03.15 |
[JavaScript] 조건문과 반복문 (0) | 2023.03.15 |
[JavaScript] 변수와 타입 (0) | 2023.03.15 |