prototypeとclassのブラウザ挙動/実装演習
プロパティの値を変える操作と、プロトタイプ参照を差し替える操作では既存実体への影響が異なります。探索の図を更新しながら、共有メソッドや共有配列の実装上の注意点を確かめます。
この単元で学ぶこと
- 親の変更・所有値・削除を組み合わせて追跡する
- prototypeの再代入と中身の変更を区別する
- 列挙と可変値共有の注意点を説明する
本文解説
探索結果は固定のコピーではない const p={n:2};const a=Object.create(p);の後にp.n=7とするとa.nも7です。aはnを所有していないので、読むたびに探索先pの現在の値を取得します。一方a.n=5を先に行えば、p.n=9の後もa.nは5です。どの行で所有キーができたかを書き込んで追います。
undefinedという値でも存在する a.n=undefinedはnを削除する操作ではありません。所有nが存在して親の同名値を隠します。"n" in aはtrueです。delete a.nは所有nを取り除くので、その後親の値が見える場合があります。所有していないnへのdeleteは通常trueを返しても、親のnを消しません。戻り値だけから探索結果まで判断しないことが大切です。
直近の一致で探索を終える root.x=1、mid=Object.create(root)、mid.x=2、leaf=Object.create(mid)ならleaf.xは2です。leafから始め、最初に一致するmidで止まります。複数の同名プロパティを合算したり、一番遠い祖先を優先したりしません。mid.xを削除した場合に初めてroot.xの1が見えるようになります。
prototypeの中身と参照を分ける function A(){};const x=new A();の後A.prototype.n=6とすると、既存xからn=6が見えます。元の共有オブジェクトを変更したからです。一方A.prototype={n:9}はAが今後使う参照の再代入です。既存xの直接プロトタイプは古いままで、新しくnewした実体が新しいオブジェクトを使います。
実体メソッドの差し替え class A{m(){return 2;}};const x=new A(),y=new A();でx.m=()=>8とすると、x.m()は8、y.m()は2です。xに作った所有mが共有メソッドを隠します。delete x.mの後x.m()は2へ戻ります。クラス全体の振舞いを変更したつもりでも、一つの実体だけを変更している可能性を確認します。
列挙可能性という別の軸 class A{m(){return 1;}}のmはA.prototypeにありますが非列挙です。Object.keys(A.prototype)にはmもconstructorも出ません。function A(){};A.prototype.m=function(){};のように通常代入で新設したmは列挙可能になります。存在・所有・列挙可能性は異なる判定なので、空のkeys配列だけでメソッド不存在とはいえません。
共有する可変値に注意する const p={items:[]};const a=Object.create(p),b=Object.create(p);ではa.itemsとb.itemsは同じ配列です。a.items.push("x")の後b.items.lengthも1です。各実体で個別の配列を持たせたいなら、コンストラクタなどでthis.items=[]と所有プロパティを作ります。継承経由で得た値の編集と、実体への新しい値の代入を区別します。
検査コード自体の前提を守る nullプロトタイプのオブジェクトにはhasOwnPropertyメソッドが継承されません。また同名キーに数値が入っているかもしれません。Object.prototype.hasOwnProperty.call(a,"x")と書けば組込みメソッドを借りて検査できます。未知のオブジェクトへa.hasOwnProperty("x")と直接呼ぶ前提を避け、データとしてのキー名と利用するメソッドを分離します。
図解
重要ポイント
- 探索は最初の一致で止まる。
- prototype再代入は既存実体の参照を変えない。
- classメソッドと通常代入では列挙可能性が違う。
- 継承した配列は参照を共有する場合がある。
間違えやすい点
- undefinedの代入をdeleteと同じと考えない。
- Object.keysの結果だけで存在を判定しない。
- 共有配列のpushを実体だけの更新と思わない。
用語解説
- 列挙可能
- Object.keysなどの列挙へ含めるかを決めるプロパティ属性。
- 参照の再代入
- 変数やプロパティが指すオブジェクトを別のものへ置き換えること。
- 共有可変値
- 複数の経路が同じオブジェクトを指し、その編集が他からも見える値。
関連教材
参考資料
教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。
- https://html5exam.jp/outline/objectives_lv2.html
- https://tc39.es/ecma262/2024/multipage/ordinary-and-exotic-objects-behaviours.html#sec-ordinarycallbindthis
- https://tc39.es/ecma262/2024/multipage/ecmascript-language-functions-and-classes.html#sec-class-definitions
- https://tc39.es/ecma262/2024/multipage/ecmascript-language-statements-and-declarations.html#sec-try-statement
確認問題の出題・採点や学習履歴の保存にはログインが必要です。