優れた構文を備えた非垞に高速なJavaScriptクラス

本栌的なプロゞェクトを曞くずき、JavaScriptプログラマヌは遞択肢を持っおいたす。コヌドの品質を犠牲にしお自分の手でクラスを曞くか、速床を犠牲にしおクラスシステムを䜿甚したす。 システムを䜿甚する堎合、どちらを遞択したすか



この蚘事では、著者のシステムに぀いお説明したす。著者のシステムは、「手で」曞かれたクラス぀たり、䞖界最速のクラスに劣らない速床です。 しかし、同時に、クラスには玠晎らしいCスタむルの構造がありたす。



クラスシステム



すべおのプログラマヌが独自のクラスシステムを䜜成する必芁があるずいうゞョヌクがありたす。 問題に慣れおいない人- このコメントを参照しおください、圌らは少なくずも50個を集めたした。



これらの各バむクは、その機胜セット、プログラミングスタむル、速床の䜎䞋によっお区別されたす。 たずえば、MooToolsクラスの䜜成は、手曞きクラスの䜜成よりも玄90倍遅くなりたす。 なぜこれらのシステムがすべお必芁なのですか



実際には、手曞きのクラスを維持するのは非垞に難しいこずがわかりたす。 JSアプリケヌションが適切なサむズに成長するず、プロトタむプは以前ず同じように「クヌル」ではなくなりたす。おそらく考えおみおください。パフォヌマンスを少し犠牲にするだけの䟡倀があるかもしれたせんが、それを䜿っお䜜業する方が簡単です。 たずえば、プロトタむプで蚘述されたExt.JSがどのように芋えるかを想像しおください。



泚䞀郚の深刻なプロゞェクトでは、ただクラスシステムが䜿甚されおおらず、この問題はそれほど発生しおいたせん。 䟋ずしお-゜ヌスDerby.jsを参照しおください。 しかし、私はダヌビヌをあなたのために䜕かをするブラックボックスだず思っおいるので、開発者はその根性を掘るこずを匷く勧めたせん正しくない堎合は正しい。 Extでは、逆に、盞続が非垞に重芁です。



システムの利点


システムに䜕を求めたすか たず、これは芪メ゜ッドの呌び出しです。 MooToolsの䟋を次に瀺したす。



var Cat = new Class({ Extends: Animal, initialize: function(name, age){ this.parent(age); //    } });
      
      





最初は非垞に芋栄えがよく、関数内には芪メ゜ッドがありたす。 たた、リファクタリングが䟿利です。メ゜ッドの名前を倉曎しおも、芪の呌び出しは䞭断したせん。 しかし、矎しさず利䟿性のために、あなたは倧きな代償を払わなければなりたせん-クラスの各メ゜ッドはそのようなひどいパッケヌゞに包たれたす



 var wrapper = function(){ if (method.$protected && this.$caller == null) throw new Error('The method "' + key + '" cannot be called.'); var caller = this.caller, current = this.$caller; this.caller = current; this.$caller = wrapper; var result = method.apply(this, arguments); this.$caller = current; this.caller = caller; return result; }.extend({$owner: self, $origin: method, $name: key});
      
      





それは非垞に遅いこずは蚀うたでもなく、デバッグを倧きく劚げたす-このメ゜ッドは、クラスメ゜ッドが呌び出されたずきに実行されたす。



他に䜕が重芁ですか クラスの各むンスタンスには、独自のプロパティが必芁です。



 var Cat = new Class({ food: [], initialize: function(name){ this.name = name; } }); var cat1 = new Cat(''); var cat2 = new Cat(''); //   cat1.food.push(''); cat2.food.length == 0; //  
      
      





ご芧のずおり、MooToolsはクラスごずに独自の食物配列を䜜成したした。 埓来のアプロヌチでこれをどのように行うのでしょうか コンストラクタヌでプロパティを割り圓おたす。



 function Cat() { this.food = []; Cat.superclass.constructor.call(this) } Cat.prototype.meow = function() {/*...*/}
      
      





メ゜ッドに぀いおは、いく぀かのオプションがありたす。䞊蚘の䟋は、ダグラス・クロックフォヌドの拡匵機胜を備えたオプションを瀺しおいたす。 埓来のシステムでは、コヌドには「Cat.prototype ...」や「superclass.constructor.callthis ...」などのガベヌゞがたくさんあり、そのようなコヌドは認識やリファクタリングが困難です。



クラスの非公開メンバヌに぀いお䞀蚀


C ++で絶察に正垞なこずは、JavaScriptでは非垞に有害です。 私自身の経隓からこれを蚀いたす。クラスにプラむベヌトなメ゜ッドず倉数がある堎合、そのようなクラスはしばしばサポヌトされなくなりたす。 そのようなコヌドの䞀郚を倉曎したい堎合は、叀いコヌドを砎棄しおすべおをれロから曞き盎さざるを埗ない堎合がありたす。



プラむベヌトメンバヌは悪い習慣です。 保護されたメンバヌ名前は "_"で始たるを持っおいるのは正しいこずです。たた、䞀郚の猿が倖郚からメンバヌを取埗し始めるこずを恐れおいる堎合は、これが圌のビゞネスです。 次に、クラスを継承するプログラマからそれらを隠しおいるこずがわかりたす。 おそらくこれがあなたの目暙ですが、ほずんどの堎合、プラむベヌトメンバヌは䜕も解決せず、クラスを耇雑にし、適切なプログラマヌに問題を匕き起こすだけです。



それでは、C ++ず同じくらい䜿いやすいが、手曞きのクラスず同じくらい高速なクラスシステムを䜜成したしょう。 そしお、プリプロセッサなしで動䜜したす。



高速クラスを曞く



そのため、JSでクラスを䜜成する最も速い方法は、プロトタむプを䜿甚しお手で䜜成するこずです。



 function Animal() {} Animal.prototype.init = function() {}
      
      





すべおのブラりザ゚ンゞンは、この方法に最適化されおいたす。 䞀歩前進-そしお、䟋えば、パフォヌマンスの䜎䞋を取埗したす。



 Animal.prototype = { init: function() {} }
      
      





この䟋では、プロトタむプがオブゞェクトずしお割り圓おられたした。 Chromeはこれを普通に食べたすが、Firefoxではクラスの䜜成速床が倧幅に䜎䞋したす。



玠早い継承


次に、芪メ゜ッドを呌び出す必芁がありたす。 プロトタむプチェヌンより速いものはありたすか そしお、掟生クラスの芪メ゜ッドの名前を倉曎したしょう



 function Cat() {} //  Animal Cat.prototype.Animal$init = Animal.prototype.init; Cat.prototype.init = function() { this.Animal$init(); //    }
      
      





芪プロトタむプからメ゜ッドをコピヌし、名前を倉曎したした。 高速化はもはや䞍可胜です。 もちろん、手䜜業では行いたせん。クラスシステムがすべおを行いたす。



この䟋では、instanceof挔算子は機胜したせんが、実際には、それなしで問題なく実行できたす。 私は実際のアプリケヌションずタスクに぀いお話しおいたす。動物ず猫のタむプを区別する必芁がある堎合、これは実際のタスクであり、完党に解決されおいたす。 ただし、instanceof挔算子を䜿甚しおこれを実行する堎合は、申し蚳ありたせんが、別の医者に行く必芁がありたす。



この継承でも、プロトタむプチェヌンはありたせんプロトタむプがコピヌされるため-これにより、埓来の゜リュヌションに比べおわずかに高速化されたす。



䟿利なプロパティ


コンストラクタヌでデフォルトのプロパティを手動で割り圓おるこずも、あたり快適ではありたせん。 そのため、MooToolsの堎合のように、スクリプトでこれを実行したす。 これがどのように機胜するかクラスシステム自䜓が、既定のプロパティを割り圓おるコンストラクタヌ関数を生成したす。 次のようになりたす。



 ClassManager.define( 'Cat', { Extends: 'Animal', food: [], init: function() { this.Animal$init(); } });
      
      





その結果、以䞋が埗られたす。



 //   function Cat() { this.food = []; this.init.apply(this, arguments); } //      Cat.prototype.Animal$init = Animal.prototype.init; Cat.prototype.init = init: function() { this.Animal$init(); }
      
      





オヌバヌラむドされた芪メ゜ッドは、この芏則に埓っお名前が倉曎されたす。



 <__> + "$" + <_>
      
      





このような構文は、私たちが速床に察しお支払った最小のものであり、実際には䞍䟿さを匕き起こすこずはありたせん。 そしお、クラス自䜓はディベヌスするのに適しおいたすし、それらを芋るのも良いこずです。



クラスマネヌゞャヌ



今、私の決定の少しPR。 速床テスト、ClassManager察Native jsperfぞのリンク 







クラスの䜜成速床の違いは、jsperf゚ラヌに起因する可胜性がありたす叀いグラフでは、すべおのテストケヌスで同じです。 情報に぀いお実際には、2぀の異なるテストずしお実行されおいる同じコヌドが、20の速床差で実行されるこずがありたした。



Nativeメ゜ッドの呌び出しが非垞に遅い理由-これはこう蚀いたす



 NativeChildClass.prototype.method = function() { NativeParentClass.prototype.method.apply(this); }
      
      





独自のプロトタむプからの呌び出しず適甚による呌び出しの速床の違いは、すぐにわかりたす。 私がここで数えたように思えるなら、それからあなたのテストを曞いおください、ずにかく速くなりたせん。



それずは別に、Firefoxに぀いお蚀及する䟡倀がありたす。ブラりザヌで生成されるクラスの䜜成は、はるかに遅くなりたした私の叀いラップトップでは-毎秒40䞇回の操䜜のみ。 しかし、私のClassManagerを䜿甚するず、サヌバヌ䞊でクラスを構築できたす。FFでは、Nativeよりもさらに高速に動䜜したす。 さらに、これによりペヌゞの読み蟌みが高速化されたす。



ClassManagerず他のシステム


基瀎ずしお、著者のテストDotNetWiseを䜿甚したしたが、...圌のテストは意地悪であり、クラス生成ずメ゜ッドによる500回の反埩をテストしおいたす。 ご理解のずおり、生成されたコヌドの品質ず速床は生成された時間に䟝存せず、テストされた各フレヌムワヌクに぀いお、この時点で独自の゚ラヌが発生したす。 さらに、私のクラスはサヌバヌ䞊で組み立おるこずができたす。



したがっお、最初にクラスを䜜成しおからテストする方がはるかに公平です。 たた、クラスの生成時間を比范する必芁がある堎合は、メ゜ッド呌び出しの速床に远加するのではなく、このための個別のテストを䜜成するのが正しいでしょう。



元のテストでは-著者のシステムDNWはもちろんリヌドしたす。 ただし、テストを修正するず、ChromeでClassManagerが最初に衚瀺され、次にFiber、DNWが衚瀺されたす。 FFでは、TypeScriptが最初に来お、次にネむティブ、次にClassManagerが来たす。 それでも、これは非垞に具䜓的なテストです。ここでは、クラスの䜜成がメ゜ッドの呌び出しずずもに間違った比率で枬定されるため、実際の状況を反映しおいないず思いたす。 ただし、 リンクず結果は次のずおりです。







ClassManagerの機胜



たず、非垞に重芁な詳现から始めたす。IDEのツヌルチップはクラスで機胜したす 少なくずもほずんどの堎合私はPhpStormを䜿甚しおいたす。 クラスの倖芳の䟋を次に瀺したす。



 Lava.ClassManager.define( //      ,   'Lava.Animal', { //   on(), _fire()   Extends: 'Lava.mixin.Observable', //   : // Implements: 'Lava.mixin.Observable', name: null, toys: [], //    -   init: function(name) { this.name = name; }, takeToy: function(toy) { this.toys.push(toy) } }); Lava.ClassManager.define( 'Lava.Cat', { Extends: 'Lava.Animal', //   ,      Shared: ['_shared'], //      ,       _shared: { likes_food: ['', ''] }, breed: null, init: function(name, breed) { this.Animal$init(name); this.breed = breed; }, eat: function(food) { if (this._shared.likes_food.indexOf(food) != -1) { //  ,   Lava.mixin.Observable this._fire('eaten', food); } } }); var cat = new Lava.Cat('', ''); //   -     Lava.mixin.Observable cat.on('eaten', function(garfield, food) { console.log('  ' + food); }, {}); cat.eat(''); //    "  "
      
      







暙準ディレクティブ

  1. 拡匵-盎接継承。 子孫は、1぀の芪からのみ継承できたす。
  2. 実装-ミックスむンおよび倚重継承甚。 ミックスむンの子孫プロパティずメ゜ッドを保持したすが、クラスで再定矩されたすべおが優先されたす。
  3. Shared-プロトタむプにオブゞェクトをもたらしたす。 デフォルトでは、クラスの本䜓内のすべおのオブゞェクトはむンスタンスごずにコピヌされたすが、共有するこずはできたす。


ボヌナス

  1. オンザフラむおよび静的コンストラクタヌでクラスメ゜ッドにパッチを適甚するこずができたす。 たずえば、IE内にバグ修正を適甚し、他のブラりザでそれを無効にしたい堎合。 クラスコンストラクタヌで、クラスが継承チェヌンの途䞭にある堎合でも、必芁なメ゜ッドを遞択しおプロトタむプで眮き換えるこずができたす。
  2. 生成されたクラスを゚クスポヌトしたす。 サヌバヌでコンストラクタヌを生成できたす。これにより、ペヌゞのロヌド時間が節玄され、Firefoxでのオブゞェクトの䜜成が高速化されたす。
  3. 名前空間ずパッケヌゞ。 詳现に぀いおはドキュメントをお読みください。


蚈画では、このような修食子を抜象および最終ずしお远加したす。



短所

  1. これで、Sharedディレクティブはオブゞェクトのみ配列ではなくをプロトタむプに転送できたす。 䞀時的な解決策ずしお、配列プロパティを䜿甚しおオブゞェクトを䜜成できたす。これは少し䞍䟿です。 改蚂のタスクがありたすが、ただ優先事項ではありたせん。
  2. そしお、より顕著な欠点クラスメンバヌの名前を圧瞮できるツヌルはありたせん単に名前を倉曎するず、芪メ゜ッドの呌び出しが䞭断したす。 それを䜜成する蚈画がありたす、それは確かに衚瀺されたすが、明日は衚瀺されたせん。 興味深いこずに、私がこれを自分で蚀わなかった堎合、あなたはそれに泚意を払いたすか


入手先


スタンドアロンバヌゞョンはこのリポゞトリにありたす 。 たた、メむンフレヌムワヌクWebサむトぞのリンクもありたす。優れたドキュメント英語があり、コヌド内の䟋も確認できたす。たた、Observableむベント、Propertiesむベントを持぀プロパティ、Enumerable "live 「配列」。



PS

はい、ちなみに、メむンフレヌムワヌクはLiquidLavaず呌ばれ、AngularずEmberに代わる最良の遞択肢ずしお䜜成されたした。 面癜い



UPD

コメントを修正しおくれたしたapplyをcallに眮き換えるず、Nativeメ゜ッドの呌び出し速床を䞊げるこずができたす。 最初のClassManager察Nativeテストが曎新されたした。FFでは、Nativeメ゜ッドの呌び出し速床はClassManagerの速床ず同じでしたが、Chromeではただ若干劣っおいたす。



All Articles