LilPacy.info

快楽な不幸を捨て苦痛まみれの幸福を求める男の物語

未分類

[JavaScript] クラス構文による独自データ型とパターンマッチ

投稿日:2021年8月29日 更新日:

生jsで素朴にオブジェクト指向プログラミングをするまでの理解の手順
jsのクラス構文による独自データ型とパターンマッチできるまでがゴール

目次

jsでの"クラス"

jsにおいて全てクラスめいたものはfunctionオブジェクトとして定義されている

          
          12
          typeof Object // -> 'function'
typeof class{} // -> 'function'

        

jsでのfunctionオブジェクト

データと機能の集合≒オブジェクト
第一級として振る舞うデータと処理のまとまり

プロトタイプベースのオブジェクト指向

__proto__がネストしていくことで継承を実現している(プロトタイプチェーン)

Screenshot - ebab7d6b799e74fccdab6016198af7ca - Gyazo

プロトタイプチェーン

  1. インスタンス作成時にインスタンスの__proto__プロパティにプロトタイプオブジェクトの参照を保存
  2. インスタンスからメソッドを参照するとき__proto__内部プロパティまで辿る

jsでのclass構文

objectとprototypeを簡単に書くための糖衣構文
実体はfunctionオブジェクト

          
          1
          typeof class {} // -> 'function'

        

class宣言とclass式、名前付きclass式の3種類
特に明示的にプロトタイプチェーンを設定しなくて済む

functionベース

          
          12345
          function Hoge(){}

function Fuga(){}

Fuga.prototype.__proto__ = Hoge.prototype;

        

classベース

          
          123456
          class Hoge {}

class Fuga extends Hoge {}

Fuga.prototype.__proto__ === Hoge.prototype
// -> true

        

なぜObject()はできてclass Hoge {} のHoge()はできないのか

そもそもビルトインオブジェクトのObjectはfunctionベースで定義
class構文ではfunctionとして呼び出されないようにHoge()でのコンストラクタ呼び出しを禁止している(ランタイムエラーになる)

          
          123
          class Hoge() {}
Hoge()
// Uncaught TypeError: Class constructor Hoge cannot be invoked without 'new' at <anonymous>:1:1

        

ビルトインオブジェクトの大半はfunctionベースでクラスめいたものとして定義されてる
が、比較的新しいMapオブジェクトとかはMap()はできない

プロトタイプメソッドとインスタンスメソッドの違い

  • 定義の仕方

    • インスタンスメソッド
      • thisに気をつけてconstructorにアロー関数で定義
    • プロトタイプメソッド
      • class宣言/式直下にメソッドとして定義 -> prototypeにメソッドが生える
  • 継承のされ方

    • インスタンスメソッド
      • constructorのsuperでインスタンスに動的にinject
    • プロトタイプメソッド
      • 親のprototypeにある定義を辿る

インスタンスメソッド > プロトタイプメソッド

同時に定義できるがオーバーライドではない
そのため、インスタンスメソッドをdeleteするとプロトタイプメソッドが参照されるようになる

          
          12345678910111213
          clsss Hoge {
  constructor(){
    this.greet = () => console.log('hello') // インスタンスメソッド
  }
  greet() { // プロトタイプメソッド
    console.log('こんにちは')
  }
}

const hoge = new Hoge()
hoge.greet() // -> 'hello'
delete hoge.greet
hoge.greet() // -> 'こんにちは'

        

プロトタイプチェーンのフォールバック

Screenshot - ad3a8b65483fafffd1325f595c21fe37 - Gyazo
dynamic languages – How does JavaScript .prototype work? – Stack Overflow

インスタンスが所属するクラスとそのクラス名の取得

          
          1234
          class Class {}
const class = new Class()
class.constructor === Class // -> true
class.constructor.name // -> "Class"

        

jsのクラス構文による独自データ型とパターンマッチ

jsで素朴なオブジェクト指向プログラミング

          
          123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
          class Animal {
    constructor(num) {
        if(num === undefined) throw 'animal type number must exist';
        this.num = Number(num)
    }

    get bark(){ throw 'bark must be implemented' }
    get fight(){ throw 'fight must be implemented' }

    get act(){
        this.bark
        this.fight
    }

    static doSomething(animal) { // like pattern matching
      if(animal.constructor === Dog) return console.log('do some dog execution');
      if(animal.constructor === Cat) return console.log('do some cat execution');
      //if(animal.constructor.name === Dog.name) return console.log('do some dog execution');
      //if(animal.constructor.name === Cat.name) return console.log('do some cat execution');
      throw 'not match any animal type'
    }

    static parse(str) {
        if(str === "Dog" || str === 0 || str === "0") return new Dog();
        if(str === "Cat" || str === 1 || str === "1") return new Cat();
        throw 'cannot parse to any animal type'
    }
}

class Dog extends Animal {
    constructor(){
        super(0)
    }
    get bark(){ return console.log("bowwow") }
    get fight(){ return console.log('bite') }
}
class Cat extends Animal {
    constructor(){
        super(1)
    }
    get bark(){ return console.log("meow") }
    get fight(){ return console.log('run away') }
}

const dog = new Dog()
const cat = new Cat()
const fish = new (class Fish{})()

dog.num // -> 0
cat.num // -> 1
//fish.num // -> undefined

Animal.doSomething(dog) // -> do some dog execution
Animal.doSomething(cat) // -> do some cat execution
// Animal.doSomething(fish) // -> Uncaught animal type number not found

Animal.parse("Dog") // -> Dog {num: 0}
Animal.parse(1) // -> Cat {num: 1}
// Animal.parse(2) // -> Uncaught cannot parse to any animal type

dog.act
// -> bowwow
// -> bite
cat.act
// -> meow
// -> run away

        

上記から特にパターンマッチ部分を抽出

          
          1234567
          static doSomething(animal) { // like pattern matching
  if(animal.constructor === Dog) return console.log('do some dog execution');
  if(animal.constructor === Cat) return console.log('do some cat execution');
  //if(animal.constructor.name === Dog.name) return console.log('do some dog execution');
  //if(animal.constructor.name === Cat.name) return console.log('do some cat execution');
  throw 'not match any animal type'
}

        

クラスの一致/不一致でパターンマッチめいたものを実装する
コンパイラの恩恵は受けれないため実装漏れはランタイムエラーを投げる

-未分類

執筆者:


comment

関連記事

no image

組織で独自の価値を発揮するには

結論、仮説、工夫、再現性を意識して仕事に臨むことではないか。 どういうことか。 誰にとっても独自なものといえば知識経験があるので、そこから独自の仮説が立つ。 また、趣味嗜好、志向性の違いから独自の工夫 …

no image

wired.jpで使われているフォントの調査

wired.jpのフォントが好きなので何を使っているのか調査してみた。 結論 Helvetica Neue(英字)、游ゴシック(日本語)というのを使っている。 CSSの指定 font-family属性 …

no image

graphvizがインポートできない

anaconda経由でgraphvizをインストール。 conda install graphviz 意気揚々と import graphviz をしてみるも、 ModuleNotFoundError …

python/matplotlibでsin/cosカーブを描く

Numpy使った方が簡単らしいが、mathモジュールで実装した方が直感的だった。 Numpyでの実装も挑戦したい。

python/matplotlibで複数グラフを描く

6年毎のニューヨークの気温を比較 参考:Pythonからはじめる数学入門