생활코딩의 JavaScript Immutability 강의를 정리하여 작성한 내용입니다.
a===b
에서 ===
(동등비교연산자)가 true
를 return
한다는 의미는 a와 b가 메모리상의 같은 공간(같은 값)을 가리킨다는 의미이다.
var p1 = 1
에서, 재할당하는 방법을 제외하면 p1에 담긴 1을 변경할 방법은 없다.
반면에
var o1 = {name: 'kim'}
에서, 재할당하지 않고도 o1
에 담긴 {name:'kim'}
을 변경할 수 있다.
→ o1.name = 'park'
var p1 = 1; // primitive type
var p2 = 1;
console.log(p1, p2, p1===p2); // 1 1 true
var o1 = {name:'kim'}; // object
var o2 = {name:'kim'};
console.log(o1, o2, o1===o2); // {name: "kim"} {name: "kim"} false
새로 변수 var p3 = p1
을 선언하면 p1
, p2
와 동일한 메모리상의 주소를 바라보고 결과적으로 같은 값( number 1)을 가지게 된다.
변수 p3
에 2라는 값을 재할당하면 p3
는 이제 p1
, p2
와는 다른 메모리 공간을 바라보고 다른 값(number 2)를 가진다.
var o3 = o1
로, var o3
를 선언하면서 변수 o1
을 할당하면 o3
는 o1
과 동일한 메모리공간을 바라보고 동일한 값({name:'kim'}
)을 가지게 된다.
★ 여기가 하일라이트, 심장이 쫄깃해지는 포인트
var o3 = o1
으로 o1
을 할당한 변수 o3
에 .
(점) 찍고 접근하여 프로퍼티 name
의 값을 'lee'
로 수정하면 o3
가 바라보고 있던 메모리공간에 담겨있는 {name:'kim'}
이 {name:'lee'}
로 변경된다.
문제는 동일한 메모리공간을 바라보고 있는 가만히 있던 변수 o1
의 값 또한 {name:'lee'}
로 변경된다는 것이다.
o1
입장에서는 황당하다. 자신은 아무것도 하지 않았는데 자신의 값이 변경되었다! (더글라스 크락포드 옹께서 왜 책 제목을 「자바스크립트는 왜 그 모양일까?」로 지었는지 알 것도 같다...)
변수 o1
이 본래 가지고 있던 값을 "불변(immutable)하게 하려면 어떻게 해야 하는가?
→ o1
의 값을 복사하여 그 값을 다른 변수에 할당한다.
var o1 = {name:'kim'};
var o2 = Object.assign({}, o1); // 간단한 Object.assign 사용법 참조
o2.name = 'lee';
console.log(o1, o2, o1 === o2); // {name: "kim"} {name: "lee"} false
간단한 Object.assign
사용법
Object.assign(target, ...sources)
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
const returnedTarget = Object.assign(target, source1, source2);
console.log("target: ", target); // target: {a: 1, b: 2, c: 3}
console.log("returnedTarget: ", returnedTarget); // returnedTarget: {a: 1, b: 2, c: 3}
참조
Nested object
객체는 프로퍼티로 구성되어 있는데, 그 프로퍼티의 값 중 하나 이상이 객체인 경우 발생할 수 있는 문제.
var o1 = { name:'kim', score:[1, 2] }
로 변수 var o1
의 프로퍼티 name에 primitive 타입인 문자를 담고, 프로퍼티 score에 object 타입인 배열 [1, 2]를 담은경우:
변수 var o1
의 값을 변수 var o2
에서도 사용하고자 Object.assign()
으로 o2
에 할당한 경우:
var o2 = Object.assign({}, o1)
{ name:'kim', score: (배열 [1, 2]가 저장된 메모리 주소) }
가 그대로 복사 된다.var o2
에 할당된 객체의 프로퍼티 score는 배열 [1, 2]
에 대해서 변수 var o1
에 할당된 객체의 프로퍼티 score가 참조하고 있는 메모리 주소를 참조하고 있다.var o2
에서 .(점) 찍고 score
프로퍼티에 접근하여 값을 변경하면 변수 var o1
의 score
프로퍼티의 값도 변경된다.o2.score.psuh(3)
으로 변수 var o2
의 프로퍼티 score
의 값을 변경하여 가만히 있던 변수 var o1
의 프로퍼티 score
의 값이 변경되는 경우
복제하려는 객체의 프로퍼티 값 중에 객체가 있고, 복제된 객체의 수정으로 원본객체의 수정이 발생하지 않게 하려면(즉, 원본 객체의 불변성을 유지하려면) 복제 대상 객체의 프로퍼티의 객체 또한 복제를 해야 한다.
o2.score = o2.score.concat() 으로 프로퍼티 score의 값인 배열까지 복제를 하면 o2.score.push(3)로 프로퍼티 score의 값을 변경하여도 변수 var o1의 프로퍼티 score의 값에는 영향이 없다.(원본이 불변이다, 원본이 immutable 하다.)
배열에 대해서 Object.assign
을 사용하면 배열의 기능들이 사라진다. 배열의 경우에는 concat()
, slice()
, Array.from()
등 복제를 하는 명령들을 사용.
간단한 concat
사용법
concat()
메서드는 인자로 주어진 배열이나 값들을 기존 배열에 합쳐서 새 배열을 반환:
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);
console.log(array3); // ["a", "b", "c", "d", "e", "f"]
참조
MDN web docs: Array.prototype.concat()
객체를 복사했지만, 객체 내부의 프로퍼티가 가진 객체(배열)는 복사되지 않은 경우
var o1 = {name:'kim', score:[1,2]};
var o2 = Object.assign({}, o1);
console.log(o1, o2, o1===o2, o1.score===o2.score);
// {name: "kim", score: Array(2)} {name: "kim", score: Array(2)} false true
객체 내부의 프로퍼티가 가진 객체(배열)까지 완전히 복사한 경우
var o1 = {name:'kim', score:[1,2]};
var o2 = Object.assign({}, o1);
o2.score = o2.score.concat();
o2.score.push(3);
console.log(o1, o2, o1===o2, o1.score===o2.score);
// {name: "kim", score: Array(2)} {name: "kim", score: Array(3)} false false