avatar
Published on

Object.freeze(), Object.seal(), Object.preventExtensions()의 차이

Author
  • avatar
    Name
    yceffort

ECMAScript 5 스펙 중에 아래와 같은 것이 있다.

  • Object.freeze()
  • Object.seal()
  • Object.preventExtensions()

얼핏보면 이름까지 비슷해보이는 세 메소드의 차이를 알기 위해서는, 객체의 구조에 대한 기본적인 지식이 있어야 한다.

객체의 구조

자바스크립트에서 객체는 특정 속성 또는 동작, 메소드를 포함할 수 있는 데이터 유형이다. 이러한 속성은 변경, 삭제, 혹은 새로운 속성 값을 추가할 수도 있다. 여기에는 두가지 유형이 있다.

  • Data Properties: 객체 내부에 정의 되어 있는 일반적인 속성을 의미한다.
  • Accessor Properties: 접근자 속성이라고도 하며, 객체의 값을 설정하거나 가져올 수 있는, getter 와 setter라고 보면된다. 이들은 get set 으로 네이밍 되어 있다.
let person = {
  firstName: 'yongchan',
  lastName: 'Kim',

  get fullName() {
    return `${this.firstName} ${this.lastName}`
  },

  set fullName(name) {
    ;[this.firstName, this.lastName] = name.split(' ')
  },
}

person.fullName = 'yc effort'

console.log(person.firstName) // yc
console.log(person.lastName) // effort

우리가 생성하는 모든 객체는, 자바스크립트 객체 생성자의 속성을 상속 받게 된다. 그 중 하나가 Object.prototype이다. prototype속성을 활용해서, 존재하는 모든 객체에 새로운 속성을 추가할 수 있다.

let person = {
  firstName: 'yongchan',
  lastName: 'Kim',

위에서 예를 든 이 객체에서, 각각의 속성은 다음과 같은 메타데이터를 가지고 있다.

  • enumerable (boolean): true 라면 loop를 돌아서 확인 가능하다.
let obj = {
  x: 1,
  y: 2,
}

Object.defineProperty(obj, 'x', {
  enumerable: false, // false
  configurable: true,
  writable: true,
  value: 1,
})

Object.defineProperty(obj, 'y', {
  enumerable: true,
  configurable: true,
  writable: true,
  value: 2,
})

Object.keys(obj) // ['y'] 만 뜬다 띠요오오오오옹
  • configurable (boolean): true 라면 재 설정이 가능하다.
let obj = {
  x: 1,
  y: 2,
}

Object.defineProperty(obj, 'x', {
  enumerable: true,
  configurable: false, // false
  writable: true,
  value: 1,
})

delete obj.x // false 가 뜨면서 삭제가 안됨
delete obj.y // true 가 리턴되고 삭제도됨
  • writable (boolean): true라면 값이 변경 될 수 있다.
let obj = {
  x: 1,
  y: 2,
}

Object.defineProperty(obj, 'x', {
  enumerable: true,
  configurable: true,
  writable: false, // false
  value: 1,
})

obj.x = 100 // 100 이 리턴되긴 하는데 수정은 안되있음
obj.y = 100 // 100 이 리턴되며 수정도 되있음

반대로, 접근자 속성은 값을 가지고 있지 않다. 이들은 get set 함수를 가지고 있다.

  • get
  • set
  • enumerable
  • configurable

값이 없기 때문에, writeable은 존재하지 않는다.

let obj = {
  x: 1,
  y: 2,
}

Object.freeze()

  • 속성을 추가할 수 없다.
  • 존재하는 속성을 삭제할 수 없다.
  • 변경할 수 없다.
  • 속성에 대해 configurable을 변경할 수도 없다. writable configurable는 false로 되어 있다.
  • prototype도 변경할 수 없다.
  • freeze() 되어 있는 객체에 변경하려고 하는 시도는 모두 에러를 내뱉는다.
  • Object.isFrozen()으로 확인이 가능하다.

Object.seal()

  • 속성을 추가할 수 없다.
  • 존재하는 속성을 삭제할 수도 없다.
  • 존재하는 속성에 대해 reconfigure할 수 없다.
  • 데이터 속성을 접근자 속성으로 바꾸거나, 그 반대로도 불가능하다.
  • 그러나 존재하는 값에 대해서 수정은 가능하다.
  • 또한 존재하는 값에 대해서 추가가 가능하다.
  • 위 아래 두 메소드와는 다르게, seal은 봉인한 객체를 리턴하므로, 해당 객체를 써야 한다.
let obj = {
  x: 1,
  y: 2,
  z: {
    a: 1,
    b: 2,
  },
}

let sealedObj = Object.seal(obj)
sealedObj.x = 100 // 100 으로 변경된다.
sealedObj.z.c = 300 // 가능.
sealedObj.a = 100 // 이건 안됨
delete sealedObj.x // 불가능

Object.preventExtensions()

전달 받은 객체를 더 이상 확장이 불가능한 상태로 만든다. 더 이상 새 속성을 추가할 수가 없다. 상위 집합 객체에서 기능을 상속한다.

let obj = {
  x: 1,
  y: 2,
  z: {
    a: 1,
    b: 2,
  },
}

Object.preventExtensions(obj)
obj.x = 100 // 100 으로 변경된다.
obj.z.c = 3 // 가능
delete obj.z // 가능. 왜냐면 확장만 막기 때문.

갑자기 이 글을 쓴 이유는 https://v8.dev/blog/react-cliff 이것 때문이다. 다음에 계속 🤔