JavaScriptでオブジェクトを保護するfreeze()とseal()の使い方

JavaScriptのオブジェクトを保護する

JavaScriptではオブジェクトがよく利用されます。

JavaScriptではAPIでJSONなどでデータを取得することが多いが、その中のデータの大半はオブジェクトです。

そのため、JavaScriptでオブジェクトの内容を保護したいことがよくあります。

constやreadonlyでは保護できない

よく誤解されるのですが、JavaScriptではconst宣言やTypeScriptのreadonlyではオブジェクトを保護できず、書き換えが可能になっています。

// JavaScript
const obj = { a: 123 }
obj.a = 444
console.log(obj.a) // 444
// TypeScript
const obj: { readonly a: number } = { a: 123 }
obj.a = 555
console.log(obj.a) // 555

※ TypeScriptの場合はtsconfig.jsonのcompilerOptionsで "noEmitOnError": true が指定されている場合はコンパイルされない。

オブジェクトの代入による書き換えやdeleteによる削除はObject.freeze()、deleteの削除だけならObject.seal()を使用すれば無効にできます。

Object.freeze()の使い方

Object.freeze()メソッドはオブジェクトを代入で変更したり、deleteで削除されないようにできます。

const obj = Object.freeze({ a: 123 })
obj.a = 444
delete obj.a
console.log(obj.a) // 123

Object.isFrozen()を使うとObject.freeze()が使われているか確認できます。

Object.isFreeze()ではないので注意が必要です。

const foo = { a: 1 }
const bar = Object.freeze({ a: 1 })
console.log(Object.isFrozen(foo)) // false
console.log(Object.isFrozen(bar)) // true

Object.seal()の使い方

Object.seal()メソッドはオブジェクトをdeleteで削除されないようにできます。

Object.freeze()と違って代入はできます。

const foo = Object.seal({ a: 1 })
foo.a = 2
console.log(foo) // {a: 2}

delete foo.a
console.log(foo) // {a: 2}

Object.isSealed()を使うとObject.seal()が使われているか確認できます。

Object.isSeal()ではないので注意が必要です。

const foo = { a: 1 }
const bar = Object.seal({ a: 1 })
console.log(Object.isSealed(foo)) // false
console.log(Object.isSealed(bar)) // true

一度使用したら解除不可

Object.freeze()およびObject.seal()を使用したオブジェクトは保護されている状態を解除できません。

Object.seal()の変数をObject.freeze()に変更することはできます。

let foo = Object.seal({ a: 1 })
foo = Object.freeze(foo)
foo.a = 2
console.log(foo) // {a: 1}

delete foo.a
console.log(foo) // {a: 1}

しかし、Object.freeze()をObject.seal()にはできません。

let foo = Object.freeze({ a: 1 })
foo = Object.seal(foo)
foo.a = 2
console.log(foo) // {a: 1}

delete foo.a
console.log(foo) // {a: 1}

すでにObject.freeze()が使われているのにObject.seal()を使ってしまうミスなどはありえるので、覚えておくと良いでしょう。