29
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【JavaScript】オブジェクトをfreezeした後でもprivateプロパティを変更できるし生やすこともできる

Last updated at Posted at 2025-10-20

Object.freeze

Object.freezeすると、そのオブジェクト直下のプロパティを作成変更削除することができなくなります。

class HOGE {
  publicProperty;
  
  setPublicProperty(val) {
    this.publicProperty = val;
  }
}

hoge = new HOGE;
hoge.setPublicProperty(10);

Object.freeze(hoge);
hoge.setPublicProperty(20);  // Uncaught TypeError: Cannot assign to read only property

そういう機能として作られたわけなのでまあ当然ですね。

privateプロパティを変更する

ところでprivateプロパティはfreeze後も変更できます。

class HOGE {
  #privateProperty;
  
  setPrivateProperty(val) {
    this.#privateProperty = val;
  }
}

hoge = new HOGE;
hoge.setPrivateProperty(30);

Object.freeze(hoge);
hoge.setPrivateProperty(40); // エラーは出ないし40になる

ええ?

これを見つけたときはけっこうびっくりだったのですが、でもこの事象について言及している記事が全く見当たらなかったので、実はわざわざ言うまでもない周知の事実だったりするのかもしれません。

MDNには一応記載がありました

プライベート要素は、現在のクラス本体内でのみアクセスすることができ、サブクラスには継承されないため、プロトタイプ継承モデルにはありません。
クラスが異なると、プライベート要素は同じ名前でも全く異なるものであり、相互運用はできません。
クラスごとに管理される、インスタンスに付加された外部メタデータとして考えてください。
このため、 structuredClone() はプライベート要素を複製せず、 Object.freeze() や Object.seal() は、プライベート要素には影響しません。

ただ、知ってから見ればなんとなくわからないでもないですが、この記述だけ読んで『privateプロパティはfreeze後も変更できる』って読み取るのは難しい気がします。

privateプロパティを生やす

かなり無理矢理ですが、freezeしたオブジェクトをexntendsしてprivateプロパティを生やすことができます。

class Base {
  constructor() {
    Object.freeze(this);
  }
}

class Extend extends Base {
  #val;
  constructor(v) {
    super();
    this.#val = v;
  }
}

const answer = new Extend(42); // 動くし42になる
Object.isFrozen(answer);       // true
Object.isSealed(answer);       // true
Object.isExtensible(answer);   // false

answer.#valは42になってしまいます。
freezeしたのにどうして。

あとどうでもいいけど、isFrozen・isSealedはfreezeされているか・sealされているかを意味するのでtrueなら制限されているのに、isExtensibleは拡張可能かなのでfalseなら制限されていることになるのでわかりにくい。
どうしてこれだけ逆なんですか?

privateプロパティを生やせないようにしよう

ということで、freezeしたオブジェクトはextendsしてもprivateプロパティを生成できないようにしようというproposalが提出されています。
タイトルはDisallow Adding New Private Fields to Non-extensible Objectsで、拡張可能でないオブジェクトにprivateフィールドを生やすことを禁止すると、まあそのまんまの意味ですね。
これが実装されたら、上記のextendsしているコードは動かなくなります。

現在の進捗は、ステージ3で仕様は決定済、あとはブラウザに実装されるのを待つだけという状態です。

ただproposalでは何故かObject.preventExtensionsにしか言及されておらず、freezeやsealについてはどういうわけか一言も書かれていません。
制限の強さは preventExtensions < seal < freeze であり、seal・freezeもNon-extensibleなので、seal・freezeにも影響すると思います。
そのあたりもちゃんとproposalに記載してほしいですね。
誰か詳しい人確認よろ。

V8ExtendingNonExtensibleWithPrivate

Chrome Platform Statusに、この機能がどれくらい使われているかを示すカウンタがありました。
2025年1月あたりに実装されたみたい。

01.png

使用率は0.00001%とかであり、まあ無視してもよさそうなレベルに見えますね。

29
12
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?