[JavaScript] 불변 객체 immutable objec

윤남주·2022년 4월 11일
0
post-thumbnail

아래 글은 책 🦁 코어 자바스크립트를 읽고 정리한 내용입니다.


불변 객체 (Immutable Object)

객체 내부 프로퍼티를 변경할 때마다

  • 새로운 객체를 만들어 재할당하기로 정하거나
  • 자동으로 새로운 객체를 만드는 도구를 활용하여 (ex. immutable.js, immer.js 등의 라이브러리)

➡️ 불변성 확보


불변 객체가 필요한 경우

객체에 변화를 가해도 원본이 그대로 남아있어야 하는 경우
ex) 정보가 바뀌었으면 알림 전송하는 경우, 바뀌기 전의 정보와 바뀐 후의 정보를 보여줘야하는 경우 등


var user = {
  name: "namju",
  gender: "male",
};

var changeName = function (user, newName) {
  var newUser = user;
  newUser.name = newName;
  return newUser;
};

var user2 = changeName(user, "yun");

// 아래의 if문은 무시되어 지나침
if (user !== user2) {
  console.log("유저 정보가 변경되었습니다.");
}

console.log(user.name, user2.name); // yun yun
console.log(user === user2); // true

➡️ 유저 정보가 변경되었음을 감지하지 못함!



불변 객체 만들기

새 객체를 하드코딩

var user = {
  name: "namju",
  gender: "male",
};

var changeName = function (user, newName) {
  return {
    name: newName,
    gender: user.gender,
  };
};

var user2 = changeName(user, "yun");

if (user !== user2) {
  console.log("유저 정보가 변경되었습니다."); // 유저 정보가 변경되었습니다.
}

console.log(user.name, user2.name); // namju yun
console.log(user === user2); // false

➡️ 👎 객체에 프로퍼티가 많을수록 하드코딩해야하는 수고가 너무 늘어남
프로퍼티 개수에 상관 없이 모든 프로퍼티를 복사하는 함수를 만드는 것이 좋음



얕은 복사

바로 아래 단계의 값들만 복사하는 방법

: for in 반복문으로 새 객체에 원래 객체의 프로퍼티들을 복사하는 함수

var copyObject = function (target) {
  var result = {};
  for (var prop in target) {
    result[prop] = target[prop];
  }
  return result;
};

이 함수를 이용해 새로운 객체를 만들어 프로퍼티를 변경할 수 있음

var user = {
  name: "namju",
  gender: "male",
};

var user2 = copyObject(user);
user2.name = "yun";

if (user !== user2) {
  console.log("유저 정보가 변경되었습니다."); // 유저 정보가 변경되었습니다.
}

console.log(user.name, user2.name); // namju yun
console.log(user === user2); // false

👎 이 방법의 단점

  • 프로토타입 체이닝 상의 모든 프로퍼티를 복사
  • getter / setter는 복사하지 않음
  • 얕은 복사만 수행함

➡️ 게다가 협업하는 모든 개발자들에게 무조건 copyObject 함수를 사용하기로 합의시키는 것이 어려움!

프로토타입 체이닝
모든 객체(함수 포함)에는 프로토타입 객체가 포함되어 있음
그렇기 떄문에 얕은 복사를 한 객체는 부모(원본) 객체의 프로토타입에도 접근할 수 있어짐
(스코프 체이닝처럼 계속 상위로 가서 탐색을 하는 식)



깊은 복사

내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법

얕은 복사 만으로는 중첩된 객체를 제대로 복사할 수 없음 (바로 아래 단계의 값들만 새로운 데이터 주소로 복사시키는 것)

var user = {
  name: "namju",
  info: {
    hobby: "bike",
    location: "seoul",
    happy: true,
  },
};

var user2 = copyObject(user);
user.name = "yun namju";
user.info.hobby = "read";
user.info.location = "busan";

console.log(user.name === user2.name); // false
console.log(user.info.hobby === user2.info.hobby); // true : 두 객체가 모두 변경됨
console.log(user.info.location === user2.info.location); // true : 두 객체가 모두 변경됨

한 단계 더 nesting 된 info 객체의 프로퍼티들에는 기존의 데이터를 그대로 참조하고 있는 것!
➡️ 이런 nested 된 모든 프로퍼티들에 대한 복사를 재귀적으로 수행해야 깊은 복사가 됨


객체의 깊은 복사를 수행하는 함수

var copyObjectDeep = function (target) {
  var result = {};

  if (typeof target === "object" && target !== null) {
    for (var prop in target) {
      result[prop] = copyObjectDeep(target[prop]); // 재귀적 호출
    }
  } else {
    result = target;
  }

  return result;
};

hasOwnProperty 메서드를 통해 프로토타입 체이닝을 통해 상속된 프로퍼티는 복사하지 않도록 할 수 있음
➕ ES6, ES2017의 경우 Object.getOwnPropertyDescriptor, Object.getOwnPropertyDescriptors를 통해 getter/setter를 복사할 수 있음


✨ 깊은 복사를 하는 다른 방법 - JSON

객체를 JSON 문법의 문자열로 만들었다가 다시 JSON 객체로 만드는 것 (JSON.stringify, JSON.parse)

  • 메서드, __proto__, getter/setter는 JSON으로 변경 불가능하여 무시됨
  • httpRequest로 받은 데이터 등의 순수한 정보를 다룰 때 활용
profile
Dig a little deeper

0개의 댓글