[TIL] [JavaScript] 자바스크립트의 불변성(Immutability)

16483 단어 JavaScriptJavaScript

불변성(Immutability)

  • 데이터의 원본이 훼손되는 것을 막는 것

데이터 타입에 따른 복사

  • 자바스크립트의 데이터타입에는 크게 기본형과 참조형이 있는데, 기본적으로 기본형은 불변값이고 참조형은 가변값이다.

1) 기본형(원시형) : 불변값

  • 위 그림에서 변수 p1와 변수p2는 데이터 영역의 같은 주소값인 1을 참조하고 있다.
    p1 === p2 // true
  • 변수 p3은 p1의 값을 복사했다
  • 변수 p3의 값이 2로 바뀌었는데, 기존 데이터 1의 값이 변한 것이 아니라 새로운 데이터를 만들어 새로운 주소값을 참조한다.

2) 참조형 : 가변값

  • 위 그림에서 변수 o1와 변수o2는 프로퍼티와 값이 동일한 객체의 값이 할당되었지만, 참조하는 주소값은 다르다.
    o1 === o2 // false
  • 변수 o3은 o1의 값을 복사했다
  • 변수 o3의 프로퍼티 name의 값이 "kim"에서 "lee"로 변경되었는데, 기존값인 o1의 값도 변하게 되었다.

불변성을 유지한 참조형 데이터의 복사

1) Object.assign()

let user = {
  name:"name",
  urls:{
    portfolio:"github link"
  }
}

let hannah = Object.assign({},user)
hannah.name = "hannah"
console.log("user", user) 
console.log("hannah", hannah)

2) 전개 구문(spread operator)

let user = {
  name:"name",
  urls:{
    portfolio:"github link"
  }
}

let hannah = {...user}
hannah.name = "hannah"
console.log("user", user) 
console.log("hannah", hannah)

  • 복사한 객체의 프로퍼티 name의 값을 변경했지만, 기존 user 객체의 데이터는 그대로 유지

✨ 중첩된 참조형 객체 복사

  • 중첩된 참조형 객체를 복사하기 위해서는 깊은 복사를 해야한다.
  • 깊은 복사는 내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법이다.

1) Object.assign() etc..

  • 복사하려는 객체의 프로퍼티를 Object.assign()로 복사해서 불변성을 유지할 수 있다.
  • 단점은 중첩된 객체가 이중, 삼중이고 여러 프로퍼티가 객체로 중첩되어 있다면 번거롭다.
let user = {
  name:"name",
  urls:{
    portfolio:"github link"
  }
}

let hannah = Object.assign({},user)
hannah.urls = Object.assign({},user.urls)
hannah.name = "hannah"
hannah.urls.portfolio = "velog link"
console.log("user", user)
console.log("hannah", hannah)

  • 예를 들어, 아래와 같이 urls의 portfolio의 값이 배열이고, 복사한 객체가 배열에 값을 push 메서드로 추가한다면...
let user = {
  name:"name",
  urls:{
    portfolio:["github","velog"]
  }
}

let hannah = Object.assign({},user)
hannah.urls = Object.assign({},user.urls)
hannah.name = "hannah"
hannah.urls.portfolio.push("google")

console.log("user", user)
console.log("hannah", hannah)

  • 기존 객체의 값 배열에도 google이 추가되었다.
  • 이 경우에는 push()메서드가 아닌 concat()메서드로 새로운 배열을 반환하면 문제를 해결할 수 있다.
hannah.urls.portfolio = hannah.urls.portfolio.concat("google")

2) JSON.parse() & JSON.stringify()

let user = {
  name:"name",
  urls:{
    portfolio:["github","velog"]
  }
}

const copyObjViaJSON = (targetObj) => {
  return JSON.parse(JSON.stringify(targetObj))
}

let hannah = copyObjViaJSON(user)
hannah.name = "hannah"
hannah.urls.portfolio.push("google")
console.log("user", user)
console.log("hannah", hannah)

  • JSON 문법으로 문자열로 전환했다가 다시 JSON 객체로 바꾸는 방법
  • 단순하고 잘 동작되지만, 메서드(함수)나 숨겨진 프로퍼티은 __proto__나 getter/setter 등과 같이 JSON으로 변경할 수 없는 프로퍼티들은 모두 무시하는 단점이 있다.

3) 재귀함수

  • 재귀: 자신을 정의할 때 자기 자신을 재참조하는 방법.
const deepCopyFunction = (inObject) => {
  let outObject, value, key

  if (typeof inObject !== "object" || inObject === null) {
    return inObject // Return the value if inObject is not an object
  }
  // Create an array or object to hold the values
  outObject = Array.isArray(inObject) ? [] : {}

  for (key in inObject) {
    value = inObject[key]

    // Recursively (deep) copy for nested objects, including arrays
    outObject[key] = deepCopyFunction(value)
  }

  return outObject
}

출처:
생활코딩 - JavaScript Immutability
코어자바스크립트 - 정재남
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
https://ko.wikipedia.org/wiki/%EC%9E%AC%EA%B7%80_(%EC%BB%B4%ED%93%A8%ED%84%B0_%EA%B3%BC%ED%95%99)
https://javascript.plainenglish.io/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089

좋은 웹페이지 즐겨찾기