sealed や final が JavaScript でも欲しく感じた
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ 親クラスでは子クラスでオーバーライドした値に応じてモードが変わる
◆ 子クラスで確定してその前提での処理なので孫クラスで変えると動かなくなる
◆ 変えたらダメってことをわかりやすく伝えたいし変えたら即エラーになってほしい
◆ 子クラスで確定してその前提での処理なので孫クラスで変えると動かなくなる
◆ 変えたらダメってことをわかりやすく伝えたいし変えたら即エラーになってほしい
継承したときに子クラスの方で上書きできないようにする機能が静的型付き言語だと見かけます
そんなのいらないんじゃない?と思って C# などを使ったときにもその機能は使ったことがなかったのですが JavaScript を使っていてちょっと欲しいなと思うことがありました
カスタムエレメントで全部の親になるベースクラスにいろいろ機能を入れたクラスを用意します
これをクラス A とします
いろいろな機能の一つに getter で取得する値によって ShadowDOM を使うかどうかというのがあります
このクラスを継承してタブとかダイアログとかボタンを各機能のベースクラスを作ります
これをクラス B とします
B では使い方が ShadowDOM がある前提とか決めてしまってるので getter で使う使わないを上書きしてしまいます
ここまでがライブラリで 実際に使うところでは B を継承した上で使う場所用のカスタマイズをした C クラスを作ります
そこでは ShadowDOM の使う使わないを変えてはいけないのですが間違って変えてしまうこともないとは言えません
変わると親の B での想定と違うのでエラーになったり変な動きになります
継承したクラスを定義するときにエラーとしてくれたほうがいいので C で上書きできないように B に sealed を設定したいです
ただ JavaScript ではただのプロトタイプチェーンでしかないのでチェーンの先にプロパティがあるとそっちが優先されます
よりチェーンの手前に来るオブジェクトに「このプロパティがあってはダメ」と指定することはできないです
自分でチェックする関数を作って実行することはできなくはないですが手間が増えますし漏れがある可能性もあります
ベースクラスなら直接 createElement で作られることもなく継承するクラスから super() でコンストラクタが呼び出されるので getter の定義を上書きするよりコンストラクタでモードを指定としたほうがいいのかもしれません
使う側が自由に毎回設定するものじゃなくて 継承したクラスで確定するのなら getter のほうが好きなのですけどね
そんなのいらないんじゃない?と思って C# などを使ったときにもその機能は使ったことがなかったのですが JavaScript を使っていてちょっと欲しいなと思うことがありました
カスタムエレメントで全部の親になるベースクラスにいろいろ機能を入れたクラスを用意します
これをクラス A とします
いろいろな機能の一つに getter で取得する値によって ShadowDOM を使うかどうかというのがあります
このクラスを継承してタブとかダイアログとかボタンを各機能のベースクラスを作ります
これをクラス B とします
B では使い方が ShadowDOM がある前提とか決めてしまってるので getter で使う使わないを上書きしてしまいます
ここまでがライブラリで 実際に使うところでは B を継承した上で使う場所用のカスタマイズをした C クラスを作ります
そこでは ShadowDOM の使う使わないを変えてはいけないのですが間違って変えてしまうこともないとは言えません
変わると親の B での想定と違うのでエラーになったり変な動きになります
継承したクラスを定義するときにエラーとしてくれたほうがいいので C で上書きできないように B に sealed を設定したいです
ただ JavaScript ではただのプロトタイプチェーンでしかないのでチェーンの先にプロパティがあるとそっちが優先されます
よりチェーンの手前に来るオブジェクトに「このプロパティがあってはダメ」と指定することはできないです
自分でチェックする関数を作って実行することはできなくはないですが手間が増えますし漏れがある可能性もあります
const sealed = Symbol("sealed")
function checkSealed(obj) {
if (obj == null) return []
const sealed_props = checkSealed(obj.__proto__)
if (sealed_props.some(e => obj.hasOwnProperty(e))) {
throw new Error("override sealed property")
}
return sealed_props.concat(obj.hasOwnProperty(sealed) ? obj[sealed] : [])
}
checkSealed.sealed = sealed
export default checkSealed
//
import checkSealed from "./check-sealed.js"
class X {
get [checkSealed.sealed]() { return ["foo", "bar"] }
get foo() { return 1 }
}
class Y1 extends X {
get foo() { return 2 }
}
class Y2 extends X {
get baz() { return 3 }
}
checkSealed(new X())
checkSealed(new Y1())
// override sealed property
checkSealed(new Y2())
ベースクラスなら直接 createElement で作られることもなく継承するクラスから super() でコンストラクタが呼び出されるので getter の定義を上書きするよりコンストラクタでモードを指定としたほうがいいのかもしれません
使う側が自由に毎回設定するものじゃなくて 継承したクラスで確定するのなら getter のほうが好きなのですけどね