JavaScriptの私有化を深く理解する


Class field declarations for JavaScript(JavaScriptクラスのフィールドステートメント)は、すでにstage-3に入っています。この中にはOOP開発者の注目すべき内容が含まれています。Private fields。JavaScriptは私有メンバーがいないのは原因がないわけではないので、この提案はJavaScriptに新たな挑戦をもたらしました。しかし同時に、JavaScriptはES 2015のリリース時にすでに私有化の問題を考えていますので、プライベートメンバーを実現するには基礎がないわけではありません。

まず穴を掘る。これはJSコードで、BusinessViewでは二つのことをやる。つまりフォームと地図をレイアウトする。
代表将兵プレフィックスの約束はプライベートです。

class BaseView {
layout() {
console.log("BaseView Layout");
}
}
class BusinessView extends BaseView {
layout() {
super.layout();
this._layoutForm();
this._layoutMap();
}
_layoutForm() {
// ....
}
_layoutMap() {
// ....
}
}

そして、業務の発展によって、地図のレイアウトが多く存在することが分かりました。ここでは継承方式を採用して実現していますので、BusinessViewから地図関連の内容を一つのベースに抽象化してMapViewといいます。

class MapView extends BaseView {
layout() {
super.layout();
this._layoutMap();
}
_layoutMap() {
console.log("MapView layout map");
}
}
class BusinessView extends MapView {
layout() {
super.layout();
this._layoutForm();
this._layoutMap();
}
_layoutForm() {
// ....
}
_layoutMap() {
console.log("BusinessView layout map");
}
}
上の二つのコードは典型的な継承に基づくOOP思想であり、本来は各階層のクラスがlayout()を通じて各階層が責任を負うべきレイアウトタスクを行うことができることを期待する。しかし、理想と現実はいつも違っています。JavaScriptで実行すると、Business Viewが発見されます。ラyoutMap()は2回実行されましたが、MapView.ulayoutMap()は実行されていません。なぜですか?
虚数関数
JavaScriptでは、先祖や子孫などに同じ名前を定義する方法があれば、デフォルトでは子孫類の中のこの方法が呼び出されます。祖先クラスの同名のメソッドを呼び出すには、子孫クラスでsuper.を通じて呼び出す必要があります。
ここでこの過程を分析してもいいです。
サブクラスがオブジェクトを作成するときには、そのクラスとすべての祖先類の定義が既にロードされています。この時
  • Business View.layoutを呼び出します。
  • super.layout()を見つけ、MapView.layout()
  • を呼び出します。
  • MapView.layout()でthis._layoutMap()
  • 、現在のオブジェクトから探します。layoutMap()
  • が見つかりました。呼び出します。
  • ほら、Business Viewで定義されています。ラyoutMapなので、プロトタイプのチェーンを探しに行きませんでした。はい、これは原型関係に基づくOOPの限界です。C〓〓〓の処理過程を見れば、違っていることが分かります。
  • Business View.layoutを呼び出します。
  • base.layout()を見つけ、MapView.layout()
  • を呼び出します。
  • MapView.layout()でthis._layoutMap()
  • MapViewで_を見つけました。layoutMap()
  • 虚数関数かどうかチェックします。
  • であれば、サブクラスに最後のオーバーロード関数を見つけて、
  • を呼び出します。
  • でなければ、直接呼び出します。
  • 違いを見つけましたか?ポイントは「虚数関数」と判断することです。
    しかし、プライベートメンバーとはどういう関係がありますか?私有関数は確かに虚数関数ではないので、C〓の中で、_layoutMapはプライベートと定義されていますが、MapView.layout()の呼び出しはMapViewです。layoutMap()
    虚関数の概念はちょっと複雑です。ただし、メンバシップ方法が虚関数として宣言された場合、その虚関数チェーンが最後の重負荷を発見して呼び出されるということは簡単に理解できる。
    JavaScriptの中では約束していますが。接頭語は私有であり、それは君子の約束であり、実質的には私有ではない。君子の約束は人に効くし、コンピューターはあなたにこの約束があるとは知らないし…。しかし、JavaScriptが本当にプライベートメンバーを実現したら、コンピュータは分かります。layoutMap()はプライベートな方法であり、サブクラスの定義を探すのではなく、このクラスの定義を呼び出すべきである。
    現在の私有化問題を解決する
    JavaScriptは現在プライベートメンバーがいませんが、プライベートメンバーの問題を解決するために有効な時間を切りたいです。どうすればいいですか?もちろん方法があります。Symbolとクローズドで解決します。
    なお、ここのクローズドは関数関数でのクローズドの生成を指示するものではありませんので、続けてみてください。
    まずはっきりさせてください。私たちはこの私有化問題に対して、祖先の調合者にある方法を呼び出させた時、まずサブクラスの中から探さないようにします。この問題は文法的には解決できません。JavaScriptは具体的な例から後の名前を探す方法です。しかし、この方法名が見つからなかったら?
    見つけられたのはメソッド名が文字列です。一つの文字列はグローバルスコープ内で同じ意味を表しています。しかし、ES 2015はSymbolを持ってきました。それは実用化されなければなりません。そして毎回実際化されるごとに、必ず違った標識を表しています。もしクラス定義を一つのクローズドにしたら、このクローズドの中でSymbolを声明して、プライベートメンバーの名前として使用して、問題は解決されます。
    
    const MapView = (() => {
    const _layoutMap = Symbol();
    return class MapView extends BaseView {
    layout() {
    super.layout();
    this[_layoutMap]();
    }
    [_layoutMap]() {
    console.log("MapView layout map");
    }
    }
    })();
    const BusinessView = (() => {
    const _layoutForm = Symbol();
    const _layoutMap = Symbol();
    return class BusinessView extends MapView {
    layout() {
    super.layout();
    this[_layoutForm]();
    this[_layoutMap]();
    }
    [_layoutForm]() {
    // ....
    }
    [_layoutMap]() {
    console.log("BusinessView layout map");
    }
    }
    })();
    
    
    現代ではモジュールの定義に基づいて、クローズドも省けます。
    
    const _layoutMap = Symbol();
    export class MapView extends BaseView {
    layout() {
    super.layout();
    this[_layoutMap]();
    }
    [_layoutMap]() {
    console.log("MapView layout map");
    }
    }
    const _layoutForm = Symbol();
    const _layoutMap = Symbol();
    export class BusinessView extends MapView {
    layout() {
    super.layout();
    this[_layoutForm]();
    this[_layoutMap]();
    }
    [_layoutForm]() {
    // ....
    }
    [_layoutMap]() {
    console.log("BusinessView layout map");
    }
    }
    
    改革後のコードは予想通りに出力できます。
    
    BaseView Layout
    MapView layout map
    BusinessView layout map
    後記
    筆者は長年の開発の過程で、問題を分析し解決する一連の思考習慣を身につけました。だから、迅速に現象を通して解決すべき本質的な問題を見て、既存の条件に基づいて解決します。確かに、Symbolの出現の理由の一つは私有化問題を解決することですが、なぜ使うべきかというと、どのように使うべきかということは分析と思考が必要です。
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。