immutability

shin·2021년 6월 27일
0

javascript

목록 보기
17/23
var p1 = 1;

p1의 값 1은 메모리 어느공간에 저장이된다.

var p2 = 1;

p2의 값은 p1에 의해 저장된 1에 의해 p1의 값을 가르킨다

그렇게되면 p1p2는 같은 값을 가르키게 된다.
비교동등연산자로 확인해보면
p1===p2 => true
같은 값인 것을 확인할 수있다.

객체 타입일 경우 어떻게 될까

var o1 = {color: 'red'};
var o2 = {color: 'red'};

먼저 비교동등연산자로 확인해보면

o1===o2 => false

false가 출력된다. 두 변수는 같지 않다는 의미이다
위에서 살펴본 숫자형인 원시타입 결과를 비춰보면
같지 않다는 의미는 두 변수의 값은 각각 다른 메모리 공간에 저장이 된다는 말이다


객체형은 color의 프로퍼티값이 언제든 바뀔수있다. 그렇기 때문에 각각 다른 메모리 공간에 값이 저장이 된다.

원시타입은 그 값이 변하지 않는다. 1 = 1이다 1 = 2가 될 수 없다. 그래서 변수의 값들이 같으면 하나의 메모리 공간만 사용을 한다. 이것을 immutability 불변성 이라고 한다.


Object assign

var p1 = 1;
var p2 = 1;
var p3 = p1;

var p3 = p1;에 의해 p3의 값은 1이 된다.
p1,p2,p3 모두 1을 가르키고 있다.

여기서 var p3 = 2; 되면
p3는 새로운 메모리 공간에 2를 저장하게 된다.

객체타입을 살펴보자

var o1 = {color: 'red'};
var o2 = {color: 'red'};
var o3 = o1

여기서 o3의 값을 blue로 바꾸고
o1,o2,o3 출력해보면

o3.color = 'blue';

console.log(o1,o2,o3);

=> {color: "blue"} {color: "red"} {color: "blue"}

원시타입과는 다르게 o1o3의 값이 같이 변경되있다.
두 값이 같이 변경되는것을 의도를 했다면 괜찮겠지만
그렇지 않는 입장에서는 곤란 할 수 있다.

이런경우 Object.assign()을 사용하면 된다.

var o1 = {color: 'red'};
var o2 = {color: 'red'};
var o3 = Object.assign({},o1);
o3.color = 'blue';

console.log(o1,o2,o3);
=> {color: "red"} {color: "red"} {color: "blue"}

o1의 값은 불변하고 o3의 값만 변경되게 된다.

중첩된 객체

var o1 = {color: 'red', score: [1,2]};

o1값으로 객체를 주었고 score의 프로퍼티값을 객체로 주었다.
객체 안에 객체가 있는 경우 메모리에 어떻게 저장이 될까

먼저 {color: 'red', score: }의 값이 메모리에 저장이되고 프로퍼티값이 배열인 객체 [1,2] 값은 따로 저장이 된다.

o1을 복제해서 o2의 값을 만들었다.

var o2 = Object.assign({}, o1);
console.log(o2);
=>{color: 'red', score: [1,2]};

메모리에는 어떻게 저장이 될까.
o2에 프로퍼티는 다른 공간에 저장이 된다. 하지만 o2 프로퍼티값 중에 object 형은 새로운 공간에 저장되지 않고 복제를 한 o1의 값을 가르킨다.
새로운 저장공간에 저장하려면 score의 객체 값을 복제해야한다.

코드로 살펴보자

var o1 = {color: 'red', score: [1,2]};
var o2 = Object.assign({}, o1);

o1을 통해 o2를 복제했고

o2.score.push(3);
복제한 o2 score값에 3을 추가하고 o1,o2를 출력하면 다음과 같이 결과값이 나온다.
{ color: 'red', score: [ 1, 2, 3 ] } { color: 'red', score: [ 1, 2, 3 ] }

o2에서 o1score값 객체를 복제하지 않았기 때문에 o2의 값을 바꿨을때 o1의 값도 같이 바뀌게 된다.
o2score값만 바꾸려고 했는데 둘다 바뀌는 곤란한 상황이 발생할 수있다.
그러면 push말고 concat을 사용하면 된다.

  • push는 인자를 추가하면 새로운 배열을 만들어내는 반면
  • concat은 배열을 복제하고 그 배열에 인자를 추가한다. 결국 배열을 복제하는 효과를 가지고있다
var o1 = {color: 'red', score: [1,2]};
var o2 = Object.assign({}, o1);

o1을 통해 o2를 복제했고

o2.score = o2.score.concat();
concat을 통해 score배열을 복제하고

o2.score.push(3);
score 배열에 3을 추가하고 o1,o2를 출력하면
{ color: 'red', score: [ 1, 2] } { color: 'red', score: [ 1, 2, 3 ] }
o1의 값을 불변하고 o2의 값만 바뀐것을 볼 수 있다.

메모리 저장공간을 살펴보면
o1, o2 모두 {color: 'red', score: }값이 각각 저장공간에 저장되었고, score의 객체인 배열은 o1을 따르는 한곳에만 저장된다. 그리고 concat에 의해 복제된 score의 값은 각각 다른 저장공간에 저장된다. 그리고 o2.score값을 변경하게 되면 o1.score은 변경되지 않고 o2의 값만 바뀌게 된다.

함수에서 활용

function fn(person){
  person.name='lee';
}
person을 인자로 같은 함수 fn을 선언했다
var o1 = {name: 'kim'}
객체를 갖는 o1을 선언했다
fn(o1);
fn함수의 인자로 o1을 넣었고 출력을 하게되면 다음과 같은 결과가 나온다
console.log(o1);
=> { name: 'lee' }

fn()의 동작 방법을 아는 사람이 사용할 때는 편리 할 수있다.
하지만 방법을 모르는 사람이 fn()을 사용 하였을때 o1의 값이 바뀌는 사고가 발생 할 수 있다. 이처럼 의도를 하지 않았는데 발생하는 효과는 좋은 코드가 아니다.

다시 코드를 작성해보면

function fn (person){
  person = Object.assign({}, o1);
  person.name = 'lee';
  return person;
}
o1이 바뀌지 않게 하기위해 o1을 복제하고 
복제한 값을 lee로 수정하고
리턴한다.
var o1 = {name: 'kim'};
var o2 = fn(o1);
console.log(o2);
=> { name: 'lee' }
function fn (person){
  person.name = 'lee';
}
var o1 = {name: 'kim'};
var o2 = Object.assign({}, o1);
fn(o2);
console.log(o2);
=> { name: 'lee' }

두 가지 방식으로 작성할 수 있다.

0개의 댓글