자료형에는 크게 2가지 종류가 있다
원시 자료형을 변수에 할당하면 메모리 공간에 값 자체가 저장된다.
원시 값을 갖는 변수를 다른 변수에 할당하면 원시 값 자체가 복사되어 전달된다.
원시 자료형은 변경 불가능한 값(immutable value)이다. 즉, 한 번 생성된 원시 자료형은 읽기 전용(read only) 값이다. 재할당을 하고 싶을 때는 새로운 메모리 공간을 확보한다.저장되었던 메모리 공간은 가비지콜렉터에 의해 자동으로 삭제된다.
// 원시 자료형(primitive type): number, string, boolean, undefined, null
42, 'string', true, undefined, null
변수 str에 재할당을 해보자
let str = 'code';
str = 'states';
숫자 타입과 마찬 가지로, 변수 str에 다른 문자열을 재할당하면 새로운 공간을 확보하고 그 공간의 이름이 str이 된다. 그 후에 문자열 ‘states’를 저장합니다. 그 이후 어느 시점에서 가비지콜렉터가 작동할 것이다.
문자열은 원시 자료형이지만 배열처럼 인덱스로 문자열의 각 문자에 접근이 가능하다.
1
2
console.log(str[0]) // 's'
console.log(str[2]) // 'a'
배열과는 달리 인덱스에 직접 다른 문자를 할당하여 값을 변경할 수 없다. 문자열은 원시 자료형이기 때문에 값을 변경할 수 없기 때문이다.
참조 자료형을 변수에 할당하면 메모리 공간에 주솟값이 저장된다.
참조 값을 갖는 변수를 다른 변수에 할당하면 주솟값이 복사되어 전달된다.
참조 자료형은 변경이 가능한 값(mutable value)이다.
// 참조 자료형(reference type)
[0, 1, 2] // 배열
{name: 'kimcoding', age: 45} // 객체
function sum (x, y) { return x + y } // 함수
참조 자료형은 변형이 가능하도록 설계되어있다.
arr[3] = '3';
arr.push(4);
arr.shift();
console.log(arr); // [1, 2, '3', 4]
let num = 5;
let copiedNum = num;
console.log(num); // 5
console.log(copiedNum); // 5
console.log(num === copiedNum); // true
copiedNum = 6;
console.log(num); // 5
console.log(copiedNum); // 6
console.log(num === copiedNum); // false
let arr = [0, 1, 2, 3];
let copiedArr = arr;
console.log(arr); // [0, 1, 2, 3]
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr) // true
slice()
로 배열을 복사할 수 있다. 새 배열은 원본 배열과 같은 요소를 갖지만 참조하고 있는 주소는 다르다.
let arr = [0, 1, 2, 3];
let copiedArr = arr.slice();
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false
Object.assign()
으로 객체를 복사할 수 있다.(얕은 복사)
let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = Object.assign({}, obj);
console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false
객체와 배열 둘 다 복사하는 방법. 변수명 앞에 ...
을 붙인다.
let arr = [0, 1, 2, 3];
let copiedArr = [...arr];
console.log(copiedArr); // [0, 1, 2, 3]
console.log(arr === copiedArr); // false
copiedArr.push(4);
console.log(copiedArr); // [0, 1, 2, 3, 4]
console.log(arr); // [0, 1, 2, 3]
let obj = { firstName: "coding", lastName: "kim" };
let copiedObj = {...obj};
console.log(copiedObj) // { firstName: "coding", lastName: "kim" }
console.log(obj === copiedObj) // false
JavaScript 내부적으로는 중첩된 구조 전체를 복사하는 깊은 복사를 구현할 수 없다. 단, 다른 문법을 응용하여 같은 결과물을 만들 수 있다. JSON.stringify()
는 참조 자료형을 문자열 형태로 변환하여 반환하고, JSON.parse()
는 문자열의 형태를 객체로 변환하여 반환. 먼저 중첩된 참조 자료형을 JSON.stringify()
를 사용하여 문자열의 형태로 변환하고, 반환된 값에 다시 JSON.parse()
를 사용하면, 깊은 복사와 같은 결과물을 반환한다. 완전한 깊은 복사를 반드시 해야 하는 경우라면, node.js 환경에서 외부 라이브러리인 lodash, 또는 ramda를 설치하면 된다
const arr = [1, 2, [3, 4]];
const copiedArr = JSON.parse(JSON.stringify(arr));
console.log(arr); // [1, 2, [3, 4]]
console.log(copiedArr); // [1, 2, [3, 4]]
console.log(arr === copiedArr) // false
console.log(arr[2] === copiedArr[2]) // false