クロージャーはあなたの友達です!
次の小さな関数を最上位の名前空間に追加するだけで、OOP の準備が整います。
- カプセル化、静的およびインスタンス、プライベートおよびパブリック変数およびメソッドを使用
- 継承
- クラスレベルの注入 (シングルトン サービスなど)
- 制約なし、フレームワークなし、単純な古い Javascript のみ
function clazz(_class, _super) {
var _prototype = Object.create((_super || function() {}).prototype);
var _deps = Array.isArray(_class) ? _class : [_class]; _class = _deps.pop();
_deps.push(_super);
_prototype.constructor = _class.apply(_prototype, _deps) || _prototype.constructor;
_prototype.constructor.prototype = _prototype;
return _prototype.constructor;
}
上記の関数は、指定されたクラスのプロトタイプと最終的な親コンストラクターを単純に接続し、インスタンス化の準備が整った結果のコンストラクターを返します。
これで、基本クラス ({} を拡張するもの) を数行のコードで最も自然に宣言でき、静的、インスタンス、パブリック、およびプライベートのプロパティとメソッドが完成します。
MyBaseClass = clazz(function(_super) { // class closure, 'this' is the prototype
// local variables and functions declared here are private static variables and methods
// properties of 'this' declared here are public static variables and methods
return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) {
// local variables and functions declared here are private instance variables and methods
// properties of 'this' declared here are public instance variables and methods
};
});
クラスを拡張しますか?より自然にも:
MySubClass = clazz(function(_super) { // class closure, 'this' is the prototype
// local variables and functions are private static variables and methods
// properties of this are public static variables and methods
return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
// local variables and functions are private instance variables and methods
_super.apply(this, arguments); // or _super.call(this, arg1, ...)
// properties of 'this' are public instance variables and methods
};
}, MyBaseClass); // extend MyBaseClass
つまり、親クラスのコンストラクターを clazz 関数に渡し、_super.call(this, arg1, ...)
必要な引数で親クラスのコンストラクターを呼び出す子クラスのコンストラクターに追加します。標準の継承スキームと同様に、親コンストラクターの呼び出しは、子コンストラクターで最初に来る必要があります。
でコンストラクターに明示的に名前を付けるか、コンストラクター内のコードからコンストラクターへの単純なアクセスが必要な場合は自由であることに注意してください。または、上記のコードのように単純にコンストラクターを返すことさえthis.constructor = function(arg1, ...) {...}
できます。あなたが最も快適に感じる方。this.constructor = function MyBaseClass(arg1, ...) {...}
return function MyBaseClass(arg1, ...) {...}
コンストラクターから通常行うように、そのようなクラスからオブジェクトをインスタンス化するだけです。myObj = new MyBaseClass();
クロージャーがクラスのすべての機能 (プロトタイプとコンストラクターを含む) を適切にカプセル化し、静的およびインスタンス、プライベートおよびパブリックのプロパティとメソッドに自然な名前空間を提供することに注意してください。クラス クロージャ内のコードには、完全に制約がありません。フレームワークも制約もありません。単純な古い Javascript だけです。閉鎖のルール!
ああ、シングルトンの依存関係 (サービスなど) をクラス (つまりプロトタイプ) に注入したい場合は、clazz
AngularJS 風にこれを行います。
DependentClass = clazz([aService, function(_service, _super) { // class closure, 'this' is the prototype
// the injected _service dependency is available anywhere in this class
return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
_super.apply(this, arguments); // or _super.call(this, arg1, ...)
// the injected _service dependency is also available in the constructor
};
}], MyBaseClass); // extend MyBaseClass
上記のコードが説明しようとしているように、シングルトンをクラスに注入するには、クラス クロージャーを、そのすべての依存関係を含む配列の最後のエントリとして配置するだけです。また、対応するパラメーターをクラス クロージャーの_super
パラメーターの前に、配列と同じ順序で追加します。clazz
配列からの依存関係を引数としてクラスクロージャに注入します。依存関係は、コンストラクターを含むクラス クロージャー内のどこでも使用できます。
実際、依存関係はプロトタイプに注入されるため、オブジェクトがクラスからインスタンス化される前であっても、静的メソッドで使用できます。これは、アプリや単体テスト、およびエンドツーエンド テストを接続するのに非常に強力です。また、コンストラクターにシングルトンを挿入する必要がなくなります。そうしないと、コンストラクターのコードが不必要に破壊されます。
このフィドルを確認してください:http://jsfiddle.net/5uzmyvdq/1/
フィードバックと提案は大歓迎です!