できるだけクラス内に関数を入れたくない
クラスを作るときは基本 this を使って値を参照・更新するのが目的なので this に依存せず 引数やグローバルなデータから返り値が決まるものはメソッドという形にはしたくないです

たとえば

class A {
something() {
// ...
this.method(this.value)
// ...
}

method(value) {
// this 使わない
return !!value
}
}

this を使うメソッド something から method を呼び出すのですが method は中で this を使わず引数だけで結果が決まります
こういう関数は A の中に入れたくないので外側に出して単純な関数にします
扱うデータが A と深い関係があるなら A の static メソッドにすることもありますが A だけに関係するわけじゃないなら別用途で使いたいときに A を通したくないので関数です

こういう感じ

const fn = (value) => {
return !!value
}

class A {
something() {
// ...
fn(this.value)
// ...
}
}

いつもは特に問題もなかったのですが 今回は少し困ったことがありました
fn みたいな関数がいくつかあり 相互に呼び出していて その深い部分で this に入ってる関数を使いたいということがありました

const foo = () => {
//
}

const bar = () => {
//
}

const baz = () => {
//
const value = getValue()
//
}

class A {
something() {
// ...
foo(this.value)
// ...
}
}

これの getValue は A のメソッドを使いたいです
また 再起や循環した呼び出しがあり baz が呼び出されるまでが長いです

イメージ:
something -> foo -> bar -> foo -> bar -> bar -> baz

foo の第二引数に getValue として使いたい A のメソッドを渡して baz が呼び出されるまで foo や bar の呼び出しで常に引数として渡すことはできるのですが すごく面倒です
また baz が呼び出されるケースは少なく その中でも getValue は初期値が必要になったときだけ使うようなもの
それのためにあちこちの呼び出しで 内部で baz を使う可能性があれば引数として A のメソッドを渡していくのはとても面倒です
React などでいう Context があると助かるのですが そういうのはないただの関数呼び出しなので都度渡していくしかないです

回避するには foo, bar, baz すべてを A のメソッドにしてしまいます
baz も A のメソッドなので this を参照できます
ただ foo, bar は A に依存しないものなので気が引けます

Node.js だと AsyncLocalStorage で Context 的なことはできるのですが ブラウザでは使えないです

const { AsyncLocalStorage } = require("node:async_hooks")

const alstorage = new AsyncLocalStorage()

const foo = () => {
bar()
}

const bar = () => {
baz()
}

const baz = () => {
const getValue = alstorage.getStore()
console.log(getValue())
}

class A {
constructor(value) {
this.value = value
}
something() {
alstorage.run(() => this.getValue(), () => {
foo()
})
}
getValue() {
return this.value
}
}

new A(1).something()
new A(2).something()
1
2
関数とクラスの置き換え
関数を受け取る関数があって それに渡す関数は特定のプロパティを持つオブジェクト返す必要がある場合

const fn = () => {
const fn1 = () => {}
const fn2 = () => {}
const value1 = 1
const value2 = 2
return { fn1, fn2, value1, value2 }
}

xxx(fn)

という感じ
クラス好きならオブジェクトは new で作ったインスタンスにすればいい

const Class = class {
fn1() {}
fn2() {}
value1 = 1
value2 = 2
}

xxx(() => new Class())

そのまま Class を渡しても new で呼び出してくれないので関数でラップしてる
オブジェクトのプロパティが関数の場合に メソッドのコンテキストで呼び出される保証がないなら this を bind するため代入形式にする

const Class = class {
fn1 = () => {}
fn2 = () => {}
value1 = 1
value2 = 2
}

xxx(() => new Class())



逆にクラスを受け取る関数の場合

const Class = class {
fn1() {}
value1 = 1
}

yyy(Class)

が通常形
関数にしたければ

const fn = () => {
const fn1 = () => {}
const value1 = 1
return { fn1, value1 }
}

const Class = function() {
return fn()
}

yyy(Class)

通常の関数はすべてアロー関数にしたいので fn はアロー関数
ただクラスとして扱うためには prototype が必要なので function で作った関数でラップする

独立したオブジェクトになるので instanceof が中で使われると正しく判定できない
instanceof に対応させる必要があるなら prototype チェーンをつなぐ

const fn = () => {
const fn1 = () => {}
const value1 = 1
return { fn1, value1 }
}

const Class = function() {
const ret = fn()
ret.__proto__ = Class.prototype
return ret
}

yyy(Class)
override と super メソッドの呼び出し
久々に WebComponents でクラスを使うとやっぱり思うのは継承の扱いづらさ
JavaScript に限らずクラス記法で書ける全言語に言えそうですが 親クラスのメソッドの呼び出しを忘れます
コンストラクタは忘れるとエラーが出ますが それでも頻繁にエラーになってます
必須ならもう自動で呼び出せばいいのに

それに加えて通常のメソッドでもありえます
こっちは呼び出さない選択ができるのでエラーにならないです
うまく動いてないと思ったら親メソッドの呼び出し忘れということが結構あります

作り上 継承する子クラスで定義する処理は親クラスの処理に追加するもので 置き換えではないことがほとんどです
自動で呼び出すように親クラス側で指定できればいいですが そういうものはないです
なのでメソッドを別にして 親クラス側では何もしないメソッドのみ子クラスで上書きするようにして親クラスのメソッドを呼び出す必要はないようにするのがいいかなと思ったのですが これはこれでメソッドが増えて管理し辛い感もあります

class A {
foo() {
// important something
}
}

class B extends A {
foo() {
super.foo()

// ...
}
}

というのがあって B の foo で A の foo を呼び出さないとけない場合 super.foo() を書かなくていいように

class A {
publicFoo() {
// important something

this.foo()
}

foo() {
// empty
}
}

class B extends A {
foo() {
// ...
}
}

とします
foo の代わりに別のメソッド publicFoo を呼び出すようにして そこで必須な処理をしてから foo を呼び出すようにします
A の foo メソッドは何もせず継承するクラスで中身を書きます

foo メソッドでは親クラスの関数を呼び出さないといけないことはないので 自身の処理だけ書けばいいです
ただ B クラス的には foo を実装してるので 別のインスタンスを呼び出すときなどは publicFoo ではなく foo を呼び出してしまいがちです
またコールスタック的に長くなるのでデバッグ時に少し不便になります
慣れないとどっちがどっちか分かりづらくなることもあります
そんな感じで これがベストと言う感じにはなってません

メソッド自体は foo のままにして super.foo を呼び出す処理を追加と考えてみてもこんな感じで 変な書き方になります

class X {
foo() {
// important something
}

extendFoo(fn) {
const foo = this.constructor.prototype.foo
return function (...args) {
foo.apply(this, args)
return fn.apply(this, args)
}
}
}

class Y extends X {
foo = this.extendFoo(function() {
// ...
})
}

メソッドは this を使うのでアロー関数でなく function を使わないといけないです
それに特殊なので面倒です
それに こういう書き方で foo を定義しないといけないとおぼえてないと普通に書いて同じ結果になります
定義方法自体が変わってるので コピペだとミスしづらいかもですが さっきの方法のほうがマシだった気がします
最近 class 全然使ってない
久々に JavaScript で class 構文を使って 最近はほぼ使うことはなくなったなと思いました
もともと class 嫌いなので積極的には使ってなかったのですが Web Components の Custom Elements で HTMLElement を継承する必要があったりで一部では使ってました
ただ Custom Elements の機能ってネイティブな HTML の要素みたいなものを自作するわけで低レイヤーよりです
考慮すべき部分も多かったり コード量も増えて気楽に使うには React や Vue みたいなものに頼るか コンポーネントに分けず lit-html を使うとかです

class を使わないというと 単純な関数だと全部を毎回引数で渡す必要があって不便に思われたりしますが class でコンストラクタで設定を受け取って保持してそれをメソッドの処理で使うというのは関数でオブジェクトを返しても一緒です
比べてみると class を使わないほうが遥かにシンプルに書けて this という厄介な存在と関わる必要もありません

例えば単純な足し算と引き算の処理で 計算結果に下限上限を設定したいとします
class だとこんな感じでしょうか

class Calc {
constructor(min, max) {
this._min = min
this._max = max
}

_fix(x) {
return Math.max(this._min, Math.min(this._max, x))
}

add(a, b) {
return this._fix(a + b)
}

sub(a, b) {
return this._fix(a - b)
}
}

const c = new Calc(10, 20)
c.add(10, 30)
// 20
c.sub(30, 25)
// 10

下限上限という設定をコンストラクタで受け取って保持して add/sub メソッドで使います
これは class を使わなくてもこう書けます

const calc = (min, max) => {
const fix = x => Math.max(min, Math.min(max, x))
return {
add: (a, b) => fix(a + b),
sub: (a, b) => fix(a - b),
}
}

const c = calc(10, 20)
c.add(10, 30)
// 20
c.sub(30, 25)
// 10

すごくスッキリしています

まぁ class でもアロー関数にすれば多少はスッキリしますけど this がいるのは変わらずです

class Calc {
constructor(min, max) {
this._min = min
this._max = max
}
_fix = (x) => Math.max(this._min, Math.min(this._max, x))
add = (a, b) =>this._fix(a + b)
sub = (a, b) =>this._fix(a - b)
}

React や Vue でも class から関数よりに変わっていってますし 特に Vue はイミュータブルで関数型という考えではなくリアクティブなオブジェクトという考えのままで this を使わなくて良くなる方法にしてますし
static プロパティへのアクセス
JavaScript のクラス構文は static プロパティへのアクセスが面倒です
this.constructor.foo のような感じになります

this.constructor を使わなくてもクラス名でのアクセスはできます
ただ クラスごとに名前が違ってコピペすると修正しないといけません
さらにクラス名を変えたときにクラス内のコードまでチェックして修正が必要になります
それを避けるために常に同じ名前でクラスのコンストラクタを取得できる仕組みが欲しいです
それが this.constructor になります

でも長くて書くのが面倒なんですよね
それを楽にするためにこういうことしてみました

const $ = class {
static foo = 1
bar() {
return $.foo + 1
}
}

export { $ as Class1 }
import { Class1 } from "./module.js"

console.log(new Class1().bar())
// 2

クラス名を使ってアクセスするのですがそれを常に $ にします
特殊な変数名ぽいので丁度いいと思います
ただクラス名が $ になってしまうと使うときに困るので エクスポートするときに適切な名前にします

モジュールに分けていると 1 つのモジュールファイルの中でクラスをいくつも定義してエクスポートすることはそうないと思いますし わりとありかなと思います
シンプルで使いやすいですし
クラスか関数か と require か import か
クラスか関数か はコレ⇩

// class
class Foo {
constructor(value) {
this.value = value
}
bar() { return this.value + 1 }
baz() { return 0 }
}
const a = new Foo(100)
a.bar()


// function
const foo = value => {
return {
bar() { return value + 1 },
baz() { return 0 },
}
}
const b = foo(100)
b.bar()

簡単に違いをまとめると

パフォーマンスはクラスのほうが prototype にまとまるので上
クラスだと最近のプライベートプロパティ機能を使わなければデータは全部見えてる
関数のほうだと return オブジェクトに含まなければ見えない

require の視点でみると関数のほうが楽

// function
const module1 = require("module")(options)

// class
const module2 = new (require("module"))(options)

new が必要なのでカッコが追加で必要になる
カッコいれないと require 自体を new することになる
いったん変数に入れてもいい

const Module = require("module")
const module3 = new Module(options)

node_modules では一旦変数に入れず require と同時にオプションを入れてインスタンスを作ってるものも多い
モジュールのインスタンスをモジュール名にしたいときはコンストラクタの名前に迷うし

でも import になると 式じゃないので取得してそのまま呼び出せない

import Module from "module"
const module4 = new Module(options)

require からの書きやすさ的な関数のメリットはなくなるし クラス構文の流行りもあって今後は new が必要なのが増えるのかも
constructor を書きたくないなら継承すればいいじゃない
前に書いたやつに関連して
constructor を書きたくないときの対処方法でこんなの思いついた

class C extends function(opt) {
this.x = opt.x
this.y = opt.y
} {
show() { console.log(this.x, this.y) }
}

new C({x: 1, y: 2}).show()
// 1 2

継承する関数だから constructor として実行される
constructor をクラス定義に書かなくていいいし
書き方的に一番上に固定されるから constructor 探す手間がはぶける
本来のプロトタイプで作るみたいに constructor とそのプロパティを別に分けられる
結構いいところが多いんだけど 関数を継承してるので 特定のクラスを継承したクラスを作れないデメリットあり

constructor を書きたくない理由は
◯ 単語が長い上に打ちづらい
◯ 他のメソッドと分けたい
◯ super() 書かないといけないのが面倒

クラスプロパティの記法だと defineProperty になって setter を使った代入にならない

class X (opt) {
x = opt.x
y = opt.y

show() { console.log(this.x, this.y) }
}

クラス構文のメソッド定義
クラス構文でプロパティ代入できるようになった結果 現状メソッド定義をする方法は主に 3 種類

class C {
value = 1

x() { return this.value }

y = function() { return this.value }

z = () => this.value
}

普通に実行するならどれも結果は同じ

const c1 = new C()
c1.x()
// 1
c1.y()
// 1
c1.z()
// 1

x はクラス構文の基本のメソッド定義方法で prototype に関数がセットされる
y と z は関数を代入してるので prototype ではなくインスタンスに関数がセットされる
prototype を直接操作したりするような場合はどっちにあるべきか考える必要あり
どっちでもいいなら prototype にあるほうが無駄が少ない

const c2 = Object.create(C.prototype)
c2.x
// f x() { return this.value }
c2.y
// undefined

y と z の違いはアロー関数を使ってるかどうか
アロー関数では arguments や this が作られない
この場合の this はそのインスタンスに固定される
メソッドを関数としてどこかに渡す場合には基本的にアロー関数にしておいたほうが良い

const { x, y, z } = c1
x()
// TypeError: Cannot read property 'value' of undefined
y()
// TypeError: Cannot read property 'value' of undefined
z()
// 1
Python のクラスの継承
クラス名のあとのカッコに親クラス名をいれる

class X:
def __init__(self):
self.a = 10

def method():
return 1

class Y(X):
def __init__(self):
self.b = 20

print(vars(Y()))
# {'b': 20}

print(Y().method())
# 1

それだけだと親クラスの初期化処理が行われなくて vars の結果に a が存在しない
初期化されてないだけで継承されてるので method メソッドは使える

他言語で言う super の実行が必要
なくてもエラーはないけど初期化されない

super() は親コンストラクタを表してるわけじゃないので super() で取得できるインスタンスの __init__ メソッド呼び出しが必要

class X:
def __init__(self):
self.a = 10

class Y(X):
def __init__(self):
super().__init__()
self.b = 20

print(vars(Y()))
# {'a': 10, 'b': 20}

子クラスの方で __init__ を書かなければ自動で親クラスが実行される
子クラスで __init__ を定義して初期化するなら 必要あるときだけ親クラスの初期化メソッドを実行できる
実行したくないならしないことができる

class X:
def __init__(self):
self.a = 10

class Y(X):
pass

print(vars(Y()))
# {'a': 10}

実行タイミングも自由に指定できる
self を使う前とか最初じゃなくていい

class X:
def __init__(self):
self.a = 10

class Y(X):
def __init__(self):
self.a = 100
super().__init__()
print(self.a)

Y()
# 10
super に渡す関数内で this を使えない
class A {
constructor(){
this.x()
}

x(){}
}

class B extends A {
x(){
console.log(1)
this.value = 1
}
}

new B()

これは問題ないコードです
A のコンストラクタ中で this.x を呼び出して B の x メソッドが呼び出され this.value を更新しています

しかし

class A {
constructor(fn){
fn()
}
}

class B extends A {
constructor(){
super(() => { this.value = 1 })
}
}

new B()

こっちはエラーです
this を使う前に super を呼び出さないといけないというものです
先に super を呼び出して入るものの super の処理が終わる前に 中で this を使う処理を呼び出すとダメみたいです

fn 呼び出しを 「Promise.resolve().then(fn)」 にしてあとで実行させると エラーはでないですが 「new B()」 の時点ではまだ実行されてないのでその後の処理に影響が出ます

super を呼び出していればその中で実行されるものはエラーにしないでほしいです
というか super 呼び出し必須という制限すら不要だと思います
やっぱり class 構文は使わないほうが扱いやすいですね