JS Object의 immutability

Marullo·2021년 4월 9일
0
post-thumbnail

객체란

객체의 정의 방식

  1. 선언적(리터럴) 형식의 정의
let obj = {
  name: "병건",
  age: 28,
};
  1. 생성자 형식의 정의
let obj = new Object();
obj.name = "병건";
obj.age = 28;
  • 두 정의 방식은 똑같은 결과를 반환한다. 다만 차이점이라고는
    생성자 형식은 한 번에 하나의 프로퍼티만 설정이 가능하다는 것이다.
    반면 리터럴 방식은 한 번에 객체를 통으로 만들 수 있음


내장 객체와 원시 타입

단순 원시 타입

  • null
  • undefined
  • boolean
  • number
  • string
  • object
  • symbol

자바스크립트에는 7개의 원시타입(primitive)이 존재한다. 이러한 단순 원시 타입은 객체가 아니다. 따라서 "자바스크립트는 모든 것이 객체다." 라는 말은 사실이 아니다.

  • typeof null === object //=> true인 것은 자바스크립트의 오랜 버그다. "단순" 원시타입은 객체가 아니다.
  • 복합 원시 타입이라는 것이 존재하는데, 이것은 객체의 하위 타입이다. 더 공부해봐야 한다.

내장 객체

내장 객체라는 객체 하위 타입이 있다.

  • String
  • Number
  • Boolean
  • Function
  • Array
  • Date
  • RegExp
  • Error
    내장 객체는 마치 클래스처럼 느껴진다. 그러나 이것들은 JS의 내장 함수(함수는 일급 객체)로 생성자로 쓰인다. 즉 이것들은 new키워드와 함께 각각 하위 객체를 생성하는 생성자들이다.

단순 원시 타입은 객체가 아니다.

var primitiveStr = "단순 원시 문자열";
var objectStr = new String("문자열 객체");

console.log(typeof primitiveStr); //=> string
console.log(typeof objectStr); //=> object

console.log(primitiveStr instanceof String); //=> false
console.log(objectStr instanceof String); //=> true
  • "단순 원시 문자열"이라는 원시 값은 객체가 아니라 원시 리터럴이며 불변값이다.
  • 따라서 원래는 String의 prototype에 연결된 메소드를 사용할 수가 없다.(인스턴스가 아니므로)
  • 그러나 JS 엔진은 상황에 맞게, 문자열 원시 값을 String 객체로 자동 강제 변환해주기 때문에, 우리가 편하게 리터럴 방식을 사용할 수 있는 것이다.



객체의 불변성

우리는 재할당을 const로 방지하려고 한다. 그러나 객체에서 const는 얕은 불변성만을 제공한다.

const obj = {
  name: "조병건",
  age: 28,
};

// obj = {}  !!Error
obj.name = "병건";
  • const로 obj 변수 자체에 다른 것이 재할당 되는 것을 막을 수는 있다.
  • 그러나 프로퍼티가 변경되는 것은 막을 수 없다. 그래서 const는 객체에게 얕은 불변성만을 제공해준다는 것이다.


프로퍼티를 불변하게

Object.defineProperties 써서 원하는 프로퍼티를 불변하게 만들 수 있다.

  • writable : false
    • true일 경우 이 속성에 설정된 값을 할당 연산자로 수정할 수 있다. 기본값은 false이다.
  • configurable : false
    • true일 경우 이 속성 서술자의 형태를 변경하거나(defineProperties를 통해 재정의하는 것), 속성을 해당 객체에서 삭제할 수 있다. 기본값은 false이다.
var obj = {};
Object.defineProperties(obj, {
  name: {
    value: "조병건",
    writable: false,
    configurable: false,
  },
  age: {
    value: 28,
    writable: true,
  },
});

obj.name = "asdasd"; //error가 나진 않음
console.log(obj.name); //변경되지 않음

obj.age = 9999;
console.log(obj.age); //변경됨


확장 금지

  • Object.preventExtensions를 사용하면, 해당 object의 프로퍼티 확장이 불가능해진다.
var obj = {
  name: "조병건",
};

Object.preventExtensions(obj);
obj.age = 28;
console.log(obj.age); //=> undefined


Object.seal() 과 Object.freeze

Object.seal()

말 그대로 객체를 "봉인"시킨다. seal()은 다음과 같은 작업을 한다.

  1. Object.preventExtentions()
    • 따라서, 새로운 프로퍼티 추가가 불가능해진다.
  2. configurable = false 모든 프로퍼티에 configurable을 false로 만든다.
    • 따라서, 프로퍼티의 값을 변경하는 것은 얼마든지 가능하다. 대신 definedProperties를 통해, 프로퍼티의 성격을 재정의 하는것은 불가능해진다.

Object.freeze()

말 그대로 객체를 "동결"시킨다. 객체를 확장할 수도 없고, 프로퍼티의 값을 변경시킬 수도 없다. 반면, 객체를 재할당 하는 것은 가능하다. 재할당의 방지는 const가 해주는 것이기 때문이다.

var obj = {
  name: "병건",
  age: 28,
};

obj.a = 123;
obj.name = "asdasdasd";
console.log(obj); // 그대로다.

obj = {};
console.log(obj); // {}

freeze 주의할 점

freeze 내부에 객체가 존재할 수 있다. freeze를 하게되면 객체 내부에 참조하고 있는 객체들을 모두 "동결"시키기 때문에(Deep freeze), 작성한 코드가 동작 안할 수 있으므로 주의하자.

profile
한국외대 중국어&컴공 복수전공 - 세미 전공자의 기술 블로그

0개의 댓글