1. ホーム
  2. ジャバスクリプト

Ext.get()とExt.fly()の違いについて

2022-03-16 21:47:01

  Extの初期からExt.flyという関数を目にしていましたが、Ext.getと変わらないと思っていたので、違いを気にしていませんでした。

これは、スピードの秘訣というよりも、「メモリ節約」のために

フライウェイトと呼ばれるもので、簡単な作業を行うことで高速化を実現します。

ブラウザのメモリーを圧迫しないことで





Flyはフライウェイトパターンを用いて、すべてのフライアウト要素がメモリを共有するようにし、プログラムの実行速度を向上させ、メモリ使用量を削減することができます。







このパラグラフは、私がこの関数に興味を持ったきっかけです。何しろ、私はしばらくJSのパフォーマンス最適化関連の問題に取り組んでいたので、"memory"という言葉には非常に敏感なのです。getとflyの実装についてExtのソースコードを見て、さらにWeb上の情報を見て、ようやく両者の類似点と相違点について理解することができました。Extの公式開発者は、以下のような説明をしていました。







Ext.Elementは、DOM要素/ノードの周りに多くの機能をラップしています。例えば、hideやshowなどの機能、すべてのアニメーション機能、ディメンションの取得と設定機能、その他多くの機能があります。







Elementは、domプロパティでラップされているDOM要素への参照を保持します。Ext.Elementを取得すると(例えばExt.get('some-id')を呼び出す)、それはElementクラスのインスタンスであり、それを使って作業することができます。







ここで、1000個のDOMノードを隠す必要があるとします。その場合、Ext.get('some-one-of-1000-id').hide()を1000回呼び出し、一つの関数:hideを呼び出すためだけに、Elementのインスタンスを1000個生成することになります。







Ext.flyは、Ext.Elementの1つのインスタンスで、その周囲を取り囲むDOMノードは"replaceable"です。Ext.fly('some-one-of-1000- id').hide() を1000回呼び出すと、Ext.Elementのインスタンスのdomプロパティが1000回置換されます。







結果:パフォーマンス向上、メモリ使用量削減。







Ext.flyが返すElementは、そのdomが遅かれ早かれ別のものに置き換わるため、後で使用するために保持することはできないということだけ心に留めておく必要があります。







この段落では、一般的に次のような考え方があります。







Element は Dom 要素のための Ext の強力なラッパーで、 dom 操作を容易にするための多くのインターフェイスをカプセル化しています (そして、Element の dom プロパティから対応する dom 要素を参照します)。そのため、作成する各 Element 要素は大量のメモリを消費します (主に、消費する操作インターフェースの数が多いため)。 したがって、あまりにも多くの Element 要素を作成すると、メモリの使用量が劇的に増加してシステム性能が下がります。







Ext.getとExt.flyはどちらもElementオブジェクトを返しますが、Ext.getは独自の操作インターフェイスを持つ別のElementを返します Package Ext.getとExt.flyは、どちらも独立した操作インタフェースを持つElementオブジェクトを返し、その戻り値を変数に保存して後で呼び出し操作などを行うことができます。これは、再利用のための利便性をもたらします。しかし、その大きな欠点として、メモリ消費の問題である Ext.get(id)を1000回呼ぶと、メモリ上に1000個の個別の要素を作成することになり、大量のメモリを消費することになる . しかし、dom要素に対してhideなどの簡単な操作しか行わない場合も多く、その都度メモリ上に別の要素を作成するのはメモリの無駄遣いになるので、1つの操作や簡単な操作しか行わない場合にExt.getを使うのは合理的ではない。 flyはこの問題を解決すべく、作成された各Elementにメモリ上の操作インターフェースのセットを共有させて、メモリを節約するために作られたものです。







以下は、Ext.flyの実装のコードです(簡単にコメントを追加しています)。







var flyFn = function(){};
flyFn.prototype = El.prototype;
var _cls = new flyFn(); // put all of Element's operational interfaces in _cls
// dom is optional
El.Flyweight = function(dom){ this.dom = dom; }; // Object containing only one dom property
El.Flyweight.prototype = _cls; // copy the operation interface to the Element instance object
El.Flyweight.prototype.isFlyweight = true; //flags the Element as a flyweight object
El._flyweights = {}; //flyweight object cache container
El.fly = function(el, named){ named = named || '_global'; el = Ext.getDom(el); //get the dom object
if(!el){ return null; }
if(!El._flyweights[named]){ El._flyweights[named] = new El.Flyweight();
//create a Flyweight object and cache it only on the first call to Ext.fly
}
El._flyweights[named].dom = el; // point the flyweight object's dom property to this el
return El._flyweights[named];
};









上記のコードから簡単にわかるのは、Ext.flyの最初の呼び出しだけがFlyweightオブジェクト(Elementのすべての操作インターフェースを含む)を作成してキャッシュし、その後のすべてのfly操作はflyweightオブジェクトのdom属性を変更するだけだということです。これにより、Ext.getと比較して、Elementを作成するたびに大量の操作インタフェースを作成する必要性が軽減されます。すべての fly オブジェクトは同じ Element インターフェイスのセットを共有するため、メモリフットプリントが非常に小さくなり、実行速度が向上します。この効果は、作成操作の数が多い場合にさらに顕著になります。







フライ操作の原理上、フライの戻り結果を変数に保存して再利用することはできません。なぜなら、フライ操作のたびに変数のドムポイントが変更される可能性が高いからです。たとえば、次のコードは不正確です。







var my_id = Ext.fly('my_id');
Ext.fly('another_id'); //at this point my_id's dom reference has changed to another_id
my_id.highlight('FF0000',{ //the operation here will be on another_id element
endColor:'0000FF', duration: 3
});









今後は、Ext.get と Ext.fly を賢く使い分け、Ext.get を "heavyweight" メソッドとして乱用しないように注意しましょう。