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

ローン/リボ払い/返済シュミレータ/Ruby

ローン、リボ払いのシミュレータをrubyで作ってみた コメントで補足もつけているためコードを参照されたし。

python/matplotlibで棒グラフを描く

該当週の歩数を棒グラフで描画 参考:Pythonからはじめる数学入門

p5.jsで球体を3行で描画

p5.jsを使うと、こんな球体がjavascript3行で書けてしまう。 以下、完成したこーど。 function setup() { createCanvas(300,300,WEBGL); sph …

no image

動機、やる気、慣性、惰性の関係性

モチベーション3.0などと謳われて久しいが、あらためて動機、モチベーション、やる気の関係性や「どうやってやる気を出せばいいのか」「やりたくないけどやらないといけないことに取り組むにはどうすればいいのか …

no image

手書き風のモデル図の書き方、作り方

パワーポイント使うとやりやすい 手順 appendix 手順 図形や矢印などを普通に書く Paperとかで下書き作るとやりやすい 透明度を調整しつつ全体観を作っていく 手書き風の図形にしたい部分を矩形 …