extJs 2.1学習ノート(Function拡張編)


ExtJsはJavaScriptの組み込みオブジェクトを拡張し、何のObject、Date、Array、Function、Stringの拡張に対して、拡張方法は皆さんが熟練しているに違いありません:prototypeの方法を使います.この記事ではFunction拡張のすばらしさについてお話ししますが、突然この問題を研究したのは、Ext.dataを研究しているからです.Storeのソースコードには、次の行が表示されます.

  this.reader.onMetaChange = this.onMetaChange.createDelegate(this);

  
当初、Ext.jsのコードを研究していたとき、Functionのいくつかの拡張について考えられず、よく分からなかったので、今日悟りました.拡張ソースコードを参照:

 createDelegate : function(obj, args, appendArgs){
        var method = this;
        return function() {
            var callArgs = args || arguments;
            if(appendArgs === true){
                callArgs = Array.prototype.slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }else if(typeof appendArgs == "number"){
                callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
                var applyArgs = [appendArgs, 0].concat(args); // create method call params
                Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
            }
            return method.apply(obj || window, callArgs);
        };
    },

 
createDelegate関数の役割は、指定した関数にコールバック関数を作成することです.新しい関数の戻りを作成し、新しい関数を返すことに注意してください.私は以前ずっと分からなかったが、どうしてこんなことをしたのか、上の赤いコードのように、みんなが私と同じように、どうしてこのように書かれていないのかと思っていた.
  this.reader.onMetaChange=this.onMetaChange;
そう書くべきではないでしょうか.dotnetを使ったことがあるならば、依頼はきっと知っていて、javascriptの中の関数はc#の依頼と同じで、とても近い意味があって、どうしてc#の中でこのように書くことができて、JavaScriptの中でこのように書くことができませんか?
すべてはthis、thisというものが風向き次第で、上のonMetaChangeという関数のように、実際に呼び出されたときにreaderにあるので、onMetaChangeでthisキーワードが使用されている場合、thisはonMetaChangeの定義環境に対応するthisではなくreaderを指す.実際、私たちは往々にしてこのthisを関数の定義環境に指そうとします.これもコールバックの最も人気のある場所ですが、thisの問題のため、コールバックは上記のように直接値を与えることはできません.関数呼び出し時にscopeが現在の定義環境になるように手足を作らなければなりません.
関数実行のscopeを変更し、JavaScriptを熟読した兄弟は必ずcall、applyを使うことを知っています.これで、createDelegateの発生背景、役割について説明しました.
createDelegate(this)、呼び出すときは、直接thisを伝えればいいのですが、本当に素晴らしいですね.実際、私が上で話したチャネルは明らかになりました.この関数のコードには秘密はありません.肝心なのはthisです.私は今、JavaScriptに対する造詣がthisに対する理解のレベルに比例していると感嘆しています.
createDelegateについて話した以上、他のいくつかの拡張関数も一緒に話しました.

  createCallback : function(/*args...*/){
        // make args available, in function below
        var args = arguments;
        var method = this;
        return function() {
            return method.apply(window, args);
        };
    }

 
呼び出し者のコールバックも作成されますが、コールバック関数のscopeはwindowです.createDelegate(window)に相当します.何も言うことはない.

defer : function(millis, obj, args, appendArgs){
        var fn = this.createDelegate(obj, args, appendArgs);
        if(millis){
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    },

   
この関数を呼び出すと、関数が1回遅延して呼び出されます.settimeoutのパッケージです.遅延パラメータが定義されていない場合は、すぐに実行します.この関数も技術的ではない.

 createSequence : function(fcn, scope){
        if(typeof fcn != "function"){
            return this;
        }
        var method = this;
        return function() {
            var retval = method.apply(this || window, arguments);
            fcn.apply(scope || this || window, arguments);
            return retval;
        };
    },

  
この関数は少し面白いですが、ext.jsの研究を始めたばかりの頃はまだ分かりませんでした.その役割は、「呼び出し関数」を呼び出し、渡された関数を呼び出す関数を返すことです.この言葉はまだはっきりしていないかもしれませんが、例を見てみましょう.
function A(){alert(「最初の実行!」return 1;}
function B(){alert("2番目の実行!");return 2;}
function C(){alert("3番目の実行!");return 3;}
  var D=A.createSequence(B).createSequence(C);
  var result=D();
上のコードの効果は次のとおりです.
最初のポップアップ表示:最初の実行!
第2のポップアップ表示:第2の実行!
3番目のポップアップ表示:3番目の実行!
resultの値は:3です.
これで皆さんもお分かりでしょう.dotnetを使ったことがあることは知っていますが、依頼変数にはこのような機能があります.累積実行の効果です.

   createInterceptor : function(fcn, scope){
        if(typeof fcn != "function"){
            return this;
        }
        var method = this;
        return function() {
            fcn.target = this;
            fcn.method = method;
            if(fcn.apply(scope || this || window, arguments) === false){
                return;
            }
            return method.apply(this || window, arguments);
        };
    }

この関数も少し面白くて、アイデアがあって、それは呼び出された関数のコールバックを返して、このコールバックは条件が実行して、実行条件はcreateInterceptorが入ってきた関数が真を返します.サンプルコードは次のとおりです.
  function A(){}
  var B=A.createInterceptor(function(i){return i>0;});
B(1)ではAが実行され、B(-1)が呼び出されるとAは実行されない.Bの役割は、入力された最初のパラメータの値が0より大きい場合にAが実行され、そうでなければ実行されないことである.
既存の関数に相当する機能は変わらず、実行条件を付けるだけです.この考えは実に巧みだ.この手は今思えば、c#にも使えます.