자바스크립트에서 원시타입은 변경 불가능한 값이다.
Boolean , null , undefined , Number , String , Symbol
원시 타입 이외의 모든 값은 Object 타입이며 객체 타입은 변경 가능한 값이다. 즉 객체는 새로운 값을 다시 만들 필요 없이 직접 변경이 가능하다.
앞서 언급한 원시타입의 값,즉 원시 값은 변경 불가능한 값이다.
주의 할 점은 원시 값 자체를 변경할 수 없다는 것이지 변수 값을 변경할 수 없다는 말은 아니다.
예를 들어
let num; 를 선언하게 되면
1.우선 undefined로 초기화가 된다.
let num = 80;
2.80을 선언하면 undefined의 값을 담고있는 메모리는 그대로 존재하게 되고 80이라는 값을 새로운 메모리에 담게되고 score라는 변수는 할당한 원시값을 가르키게 된다.
num = 100;
3.값을 재할당 하게 되면 메모리에는 undefined , 80이 있고 num이라는 변수가 새롭게 할당된 100이라는 변수를 가르킨다.
즉 원시값 자체는 변경될 수 없는 값이기 때문에 값을 재할당하게되면 새로운 매모리공간을 만들어서 변수를 할당하게 한다. 값의 이러한 특성을 불변성이라고 한다.
불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없다.
객체(참조) 타입의 값, 즉 객체는 변경 가능한 값이다.
예를 들어
let person = {
name: 'Lee';
}
let copy = person;
이라고 할 때
1.person이라는 변수에는 name 프로퍼티가 있는 참조값이 저장된다.
즉 변수처럼 값이 들어있는 메모리가 저장되는게 아니라
객체에 접근할 수 있는 참조값이 저장되는 것이다.
2.copy라는 변수에 참조값을 복사하게 되면 copy라는 변수에도 name 프로퍼티에 접근할 수 있는 참조값이 저장된다.
3.copy라는 변수를 통해서도 name 프로퍼티가 있는 객체를 수정할 수 있고
pesron이라는 변수를 통해서도 name 프로퍼티가 있는 객체를 수정할 수 있다.
즉 두개의 식별자가 하나의 객체를 공유한다는 것이다.
얕은 복사는 객체의 참조값(주소값)을 복사하고 , 깊은 복사는 객체의 실제 값을 복사한다.
원시값을 복사할 때 그 값은 또 다른 독립적인 메모리 공간에 할당하기 때문에, 복사를 하고 값을 수정해도 기존 원시값을 지정한 변수에는 영향을 끼치지 않는다.
실제 값을 복사하는 것을 깊은 복사라고 한다.
원시값의 깊은 복사
const a = 'a';
let b = 'b';
b = 'c';
console.log(a); // 'a';
console.log(b); // 'c'; -> 기존 값에 영향을 끼치지 않는다.(메모리의 값)
참조값을 복사할 때는 변수가 객체의 참조를 가리키고 있기 때문에 복사된 변수 또한 객체가 저장된 메모리 공간의 참조를 가리키고 있다. 복사를 하고 객체를 수정하면 두 변수는 똑같은 참조를 가리키고 있기 때문에 기존 객체를 저장한 변수에 영향을 끼친다.
객체의 참조값(주소값)을 복사하는 것을 얕은 복사라고 한다.
const a = { one: 1, two: 2,};
let b = a;
b.one = 3;
console.log(a); // { one: 3, two: 2 } 출력
console.log(b); // { one: 3, two: 2 } 출력
// 기존 값에 영향을 끼친다.
얕은 복사(shallow Copy)방법
-> 객체를 복사할 때 기존 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말한다.
객체 안에 객체가 있을 경우(중첩객체) 한 개의 객체라도 기존 변수의 객체를 잠조하고 있다면 이를 얕은 복사라고 한다.
-> 한 단계 까지만 복사
대표적인 예
Array.prototype.slice()
cf)JSON
->클라이언트와 서버 간의 HTTP통신을 위한 텍스트 데이터 포멧
{
"name":'lee',
"age":20,
"alive":true,
"hobby":["traveling","tennis"]
}
키는 반드시 큰 따옴표(작은 따옴표 불가)로 묶어야 하고 나머지 값들은 객체 리터럴과 같은 표기법을 그대로 사용할 수 있지만 문자열은 반드시 큰 따옴표를 사용해야 한다.
JSON.stringfy()
-> 객체를 JSON포맷의 문자열로 변환한다.
-> 클라이언트에서 서버로 객체를 전송하려면 객체를 문자열화 해야한다.
-> 직렬화
JSON.parse()
-> JSON 포맷의 문자열을 객체로 변환한다.
-> 서버에서 클라이언트에게 전송되는 데이터 문자열
-> 클라이언트에서 사용하기 위해서 JSON포맷의 문자열을 객체화 해야한다.
-> 역직렬화
const original = ['a',2,true,4,"hi"];
const copy = original.slice();
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
copy.push(10);
console.log(JSON.stringify(original) === JSON.stringify(copy)); // false
console.log(original); // [ 'a', 2, true, 4, 'hi' ]
console.log(copy); // [ 'a', 2, true, 4, 'hi', 10 ]
slice 메서드는 기본적으로 얕은 복사를 수행하지만 1차원 배열의 경우 값이 원시 타입이기 때문에 원시 타입에 새로운 값 할당 시 독립적인 메모리 공간을 생성하여 이 메모리 공간에 새로운 데이터가 추가되어 서로 다른 참조를 바라보게 된다. 그래서 깊은 복사처럼 보인다.
하지만 2차원 배열의 경우 내부 배열도 참조 타입이기 때문에 얕은 복사가 된다.
const original = [ [1, 1, 1, 1], [0, 0, 0, 0], [2, 2, 2, 2], [3, 3, 3, 3],];
const copy = original.slice();
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
copy[0][0] = 99;
copy[2].push(98);
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
console.log(original);// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
console.log(copy);// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
대표적인 얕은 복사 방법
-> slice() , Object.assingn(), Spread 연산자()
깊은 복사(Deep Copy)
->객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.
->객체에 중첩되어 있는 객체까지 모두 복사
JSON.parse(JSON.stringfy(obj)) , Lodash 라이브러리의 cloneDeep 메소드
쉽게 얘기하면 기본 자료형 , 1차원 배열은 깊은 복사가 일어나고
const와 Object.freeze()를 조합하여 만들거나
Object.freeze()를 재귀적으로 호출하는 방법이 있다.