number와 string과 같은 기본형 변수의 경우에도 해당 변수의 메모리주소에는 포인터가 들어있다. 포인터는 불변값을 저장하는 데이터 영역
의 어느 공간을 가리킨다. 포인터를 따라 데이터 영역에 접근하면 비로소 원하는 기본형의 값을 확인 할 수 있다. 아래와 같은 코드를 작성하면 자바스크립트는 내부적으로 다음과 같이 동작한다.
데이터 영역
에 새로운 문자열 'abcdef'를 저장하는 공간을 만든다.let a = 'abc';
a = a + 'def';
정확히 말하면 자바스크립트의 기본형 변수도 내부적으로는 참조형처럼 동작한다.
변수에서 해당 데이터를 참조하면 데이터 영역에 해당 값을 생성한다. 데이터 영역에 생성된 해당 값은 변경불가능하다. 만약 변수에서 값을 바꾸면 내부적으로는 다른 데이터를 생성하고, 변수의 포인터만 변경한다.
let myStr = 'str1';
myStr = 'str2`;
객체와 배열과 같은 참조형 데이터는 (일반적으로는) 가변적이다. (의도 따라 불변값처럼 활용하게 코딩할 수도 있다.)
아래의 코드를 실행했을 때 메모리의 동작과정을 살펴보자.
let obj1 = {
a : 1,
b: 'bbb',
}
변수영역
에 생성되고, 해당 공간에는 포인터가 저장된다.데이터영역
에는 객체 내부의 변수들을 가리키는 포인터가 저장된다. 여기서는 a, b각각변수영역
에 있는 객체의 구성요소들을 찾을 수 있다.변수영역
에 있는 객체의 각 구성요소들은 이름과 값을 가지고 있는데, 값은 데이터영역
을 가리키는 포인터다.데이터영역
의 해당 메모리를 확인하면 객체의 실제값을 확인할 수 있다.변수영역
과 데이터영역
은 저자가 이해를 돕기 위해 만든 개념이다. 불변객체는 데이터영역에 저장된 불변값에 대한 포인터를 가지고 있다. 가변객체는 데이터영역에 저장된 가변객체에 대한 포인터를 갖는데, 그 포인터가 가리키는 공간에는 또 다시 주소값이 들어있다.
이곳을 많이 참고해서 작성했습니다.
객체가 불변하다는 것(immutable)은 객체가 최초 생성되었을 때의 값이 변하지 않고 유지된다는 의미다. 객체의 불변성을 지키면 원본 데이터가 변경, 훼손되는 것을 막을 수 있다.
복잡한 코드에 전역 변수가 많을 경우 변수의 값이 변경되었을 때 그 변수를 사용하는 곳에서 의도치 않게 값이 변경될 수도 있고, 해당 변수가 어디 있는지 추적하기도 어려워진다. 객체의 불변성을 지키지 않고 참조 값을 여러 객체가 공유할 경우 어떤 객체에서 값이 변경되었을 때 의도치 않게 다른 객체에서도 값이 변경되며, 변경된 곳을 추적하기 어려워진다.
특히 리액트의 가상 DOM은 이전 상태와 비교해서 변경된 부분만 리렌더링하는 형태인데 이를 위해선 기존 객체가 불변한 상태에서 새로운 객체와 값을 비교해야 한다. 특히 state는 객체 형태이므로 내부 속성값만 변경된다면 react은 state가 변화한 것을 인식하지 못하고 리렌더링을 수행하지 않는다.
객체나 배열은 기본적으로 가변객체이므로, 이를 불변객체로 취급하려면 특별한 유틸리티 함수가 필요하다.
얕은 복사의 경우 nested objects를 완벽히 복사할 수 없다.
function copyObject(target) {
let result = {};
for (let prop in target) {
result[prop] = prop;
}
return result;
}
nested object를 완전히 복사하려면 아래와 같이 재귀적으로 작동하는 함수를 활용해야 한다.
function deepCopyObject(target) {
let result = {};
if (typeof target === 'object' && target !== null) {
for (let prop in target) {
result[prop] = deepCopyObject(prop);
}
} else { // 객체가 아닌 경우에는 타겟을 그대로 반환가능.
result = target;
}
return result;
}
순수한 정보만 들어가 있는 객체를 복사할때 유용한다. 메서드나 숨겨진 프로퍼티들은 복사되지 않는다.
function deepCopyObjectWithJSON(target) {
return JSON.parse(JSON.stringify(target));
}
_ = require('lodash')
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
null
을 활용하자.