Node.jsの継承に関する簡単なメモ

JavaScriptには、継承、クラスとプロトタイプ、ファクトリーと不純物、直接と間接、およびいくつかのメソッドのハイブリッドの多くの異なるメソッドがあります。 ただし、Node.jsにはutil.inherits (ChildClass、ParentClass)を使用するネイティブな方法があります。 最近まで、私は組み込みメソッドにのみNodeメソッドを使用し(EventEmitter、Readable / Writable Stream、Domain、Bufferなどの独自の子孫を作成する必要があったとき)、すべてのJavaScriptに共通のプラクティスを使用してサブジェクトエリアをモデル化しました。 そして今、初めて、組み込みの継承クラスではなく、ドメインクラスではなく、 Impressアプリケーションサーバーのシステムコードで大きな影響を受けるクラスのシステムクラスの独自の階層を実装する必要がありました。 どういうわけか、util.inheritsの使用だけでは不十分でした。記事を検索し、必要なものを完全に見つけられませんでした。彼女もあなたを助けてくれることを願っています。 煩雑さのために、子クラスでオーバーライドされたメソッドから親クラスのメソッドを呼び出す実装が好きではないことをすぐに警告します。したがって、代替メソッドを歓迎し、リポジトリまたはこのノートへのコメントで招待します。





実装要件:



基本的な例

継承によって接続された2つのクラスがあり、子のコンストラクターからClassName.super_.apply(this、arguments)を介して親クラスのコンストラクターを呼び出します。 当然、この呼び出しは、子の最初、コンストラクター、およびその終わりまたは中間の両方で行うことができます。 呼び出しは条件にラップできます。 祖先のコンストラクターの機能へのロールバックを完全に制御します。



var util = require('util'); //   function ParentClass(par1, par2) { this.parentField1 = par1; this.parentField2 = par2; } function ChildClass(par1, par2) { ChildClass.super_.apply(this, arguments); this.childField1 = par1; this.childField2 = par2; } //  util.inherits(ChildClass, ParentClass); //        var obj = new ChildClass('Hello', 'World'); console.dir({ obj: obj }); /* : { obj: { parentField1: 'Hello', parentField2: 'World', childField1: 'Hello', childField2: 'World' } } */
      
      





拡張例

ここでは、プロトタイプを介して、親クラスと子の両方のメソッドとプロパティをすでに定義しています。 これらは、生成されたインスタンスのメソッドとプロパティではなく、クラス自体のメソッドとプロパティになることを思い出させてください。 それらは標本に表示されますが、プロトタイプに含まれています。 コンソールの結論は、すべてが正常に機能し、便利で予測可能なものであることを示しています。



 var util = require('util'); //    function ParentClass(par1, par2) { this.parentField1 = par1; this.parentField2 = par2; } //    ParentClass.prototype.parentMethod = function(par) { console.log('parentMethod("' + par + '")'); }; //    ParentClass.prototype.parentField = 'Parent field value'; //    function ChildClass(par1, par2) { ChildClass.super_.apply(this, arguments); this.childField1 = par1; this.childField2 = par2; } //  util.inherits(ChildClass, ParentClass); //    ChildClass.prototype.childMethod = function(par) { console.log('childMethod("' + par + '")'); }; //    ChildClass.prototype.childField = 'Child field value'; //      var parentClassInstance = new ParentClass('Marcus', 'Aurelius'); var childClassInstance = new ChildClass('Yuriy', 'Gagarin'); //   console.dir({ parentClassInstance: parentClassInstance, childClassInstance: childClassInstance }); console.dir({ objectFieldDefinedInParent: childClassInstance.parentField1, classFieldDefinedInParent: childClassInstance.parentField, objectFieldDefinedInChild: childClassInstance.childField1, classFieldDefinedInChild: childClassInstance.childField }); parentClassInstance.parentMethod('Cartesius'); childClassInstance.childMethod('von Leibniz'); /* : { parentClassInstance: { parentField1: 'Marcus', parentField2: 'Aurelius' }, childClassInstance: { parentField1: 'Yuriy', parentField2: 'Gagarin', childField1: 'Yuriy', childField2: 'Gagarin' } } { objectFieldDefinedInParent: 'Yuriy', classFieldDefinedInParent: 'Parent field value', objectFieldDefinedInChild: 'Yuriy', classFieldDefinedInChild: 'Child field value' } parentMethod("Cartesius") childMethod("von Leibniz") */
      
      





メソッドのオーバーライドの例

さらに興味深いことに、ParentClassにはmethodNameメソッドがあり、新しいオーバーライドされた実装から祖先メソッドを呼び出す機能を使用して、ChildClassの子孫で再定義する必要があります。



 var util = require('util'); //    function ParentClass(par1, par2) { this.parentField1 = par1; this.parentField2 = par2; } //    ParentClass.prototype.methodName = function(par) { console.log('Parent method implementation: methodName("' + par + '")'); }; //    function ChildClass(par1, par2) { ChildClass.super_.apply(this, arguments); this.childField1 = par1; this.childField2 = par2; } //  util.inherits(ChildClass, ParentClass); //      ChildClass.prototype.methodName = function(par) { //     ChildClass.super_.prototype.methodName.apply(this, arguments); //   console.log('Child method implementation: methodName("' + par + '")'); }; //     var childClassInstance = new ChildClass('Lev', 'Nikolayevich'); //   childClassInstance.methodName('Tolstoy'); /* : Parent method implementation: methodName("Tolstoy") Child method implementation: methodName("Tolstoy") */
      
      



親クラスのメソッドを呼び出すためのこの構築:ClassName.super_.prototype.methodName.apply(これ、引数)は確かに非常に面倒ですが、ネイティブノードの継承の実装に別の方法は見つかりませんでした。 私に起こった唯一の疑わしい改善は、次の例で与えられます。



継承の代替方法

先祖メソッドを呼び出す構文を簡素化するために、オーバーライドメソッドをFunction基本クラスに追加して価格を支払う必要があります。 一般的なすべての関数(ノードの現在のコンテキスト、またはサンドボックス/サンドボックス内、これがすべてシールドメモリコンテキストで実行されるコード-サンドボックス内で発生する場合) その後の呼び出しはエレガントになります:this.inherited(...)またはユニバーサルオプションthis.inherited.apply(this、arguments)を使用できます。このオプションでは、すべてのパラメーターを名前で親メソッドの呼び出しに置き換える必要はありません。



 var util = require('util'); //     Function.prototype.override = function(fn) { var superFunction = this; return function() { this.inherited = superFunction; return fn.apply(this, arguments); }; }; //    function ParentClass(par1, par2) { this.parentField1 = par1; this.parentField2 = par2; } //    ParentClass.prototype.methodName = function(par) { console.log('Parent method implementation: methodName("' + par + '")'); }; //    function ChildClass(par1, par2) { ChildClass.super_.apply(this, arguments); this.childField1 = par1; this.childField2 = par2; } //  util.inherits(ChildClass, ParentClass); //      ChildClass.prototype.methodName = ParentClass.prototype.methodName.override(function(par) { //     this.inherited(par); //  this.inherited.apply(this, arguments); //   console.log('Child method implementation: methodName("' + par + '")'); }); //     var childClassInstance = new ChildClass('Lev', 'Nikolayevich'); //   childClassInstance.methodName('Tolstoy'); /* : Parent method implementation: methodName("Tolstoy") Child method implementation: methodName("Tolstoy") */
      
      





UPD:メソッドのオーバーライドを伴う最適なオプション

xdenserとともに、 super_を使用せず、適用する必要のない、最も高速でかなり簡潔なバージョンが見つかりました。



 var util = require('util'); //     function override(child, fn) { child.prototype[fn.name] = fn; fn.inherited = child.super_.prototype[fn.name]; } //    function ParentClass(par1, par2) { this.parentField1 = par1; this.parentField2 = par2; } //    ParentClass.prototype.methodName = function(par) { console.log('Parent method implementation: methodName("' + par + '")'); console.dir({t1:this}) this.parentField3 = par; }; //    function ChildClass(par1, par2) { ChildClass.super_.call(this, par1, par2); this.childField1 = par1; this.childField2 = par2; } //  util.inherits(ChildClass, ParentClass); //      override(ChildClass, function methodName(par) { //     methodName.inherited.call(this, par); //   console.log('Child method implementation: methodName("' + par + '")'); this.childField3 = par; }); //     var childClassInstance = new ChildClass('Lev', 'Nikolayevich'); //   childClassInstance.methodName('Tolstoy'); /* : Parent method implementation: methodName("Tolstoy") Child method implementation: methodName("Tolstoy") */
      
      





性能比較

望ましいオプションは明らかですが、測定を行う必要があります。 同じ機器で先祖クラスメソッドを呼び出す10,000,000回の呼び出し:

ロシア語と英語のコード例とコメントを含むリポジトリ: https : //github.com/tshemsedinov/node-inheritance



All Articles