fnText Magic

上一篇忘了提這個很特別的function

fnTest = /xyz/.test(function() {
    xyz;
}) ? /\b_super\b/ : /.*/,

//after several lines

prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ?

大概可以推測他想做的是從字串找到._super,但是那個詭異的fnTest實在不容易懂。

後來在一個對岸熱血青年(他把所有js lib的類別實作都研究過了,這也是我之後想做的事)的文章中看到了曙光:http://www.cnblogs.com/sanshi/archive/2009/07/09/1519585.html

簡單的說利用了隱含型別轉換來作regExp測試,測試什麼?新的類別定義中,所有函數裡面有寫到this._super的話,去把this._super替換掉,變成舊類別的同名函數。

proxy

以下節錄自jmvcdoc中proxy的說明:

jQuery.Class.static.proxy
Source

Returns a callback function for a function on this Class. Proxy ensures that 'this' is set appropriately.

$.Class("MyClass",{
    getData: function() {
    this.showing = null;
    $.get("data.json",this.proxy('gotData'),'json')
    },
    gotData: function( data ) {
    this.showing = data;
    }
},{});
MyClass.showData();
Currying Arguments

Additional arguments to proxy will fill in arguments on the returning function.

$.Class("MyClass",{
   getData: function( callback ) {
     $.get("data.json",this.proxy('process',callback),'json');
   },
   process: function( callback, jsonData ) { //callback is added as first argument
       jsonData.processed = true;
       callback(jsonData);
   }
},{});
MyClass.getData(showDataFunc)
Nesting Functions

Proxy can take an array of functions to call as the first argument. When the returned callback function is called each function in the array is passed the return value of the prior function. This is often used to eliminate currying initial arguments.
$.Class("MyClass",{
getData: function( callback ) {
//calls process, then callback with value from process
$.get("data.json",this.proxy(['process2',callback]),'json')
},
process2: function( type,jsonData ) {
jsonData.processed = true;
return [jsonData];
}
},{});
MyClass.getData(showDataFunc);

Currying是什麼?

在javascript pattern一書中有提到Currying,
這個名詞來自於一個數學家Haskell Curry,Currying指的是函式轉換(的過程)。

Code Trace to proxy

proxy source code如下:

proxy: function(funcs) {

    //args that should be curried
    var args = makeArray(arguments),
    self;

    // get the functions to callback
    funcs = args.shift();

    // if there is only one function, make funcs into an array
    if (!isArray(funcs)) {
    funcs = [funcs];
    }

    // keep a reference to us in self
    self = this;

    return function class_cb() {
    // add the arguments after the curried args
    var cur = concatArgs(args, arguments),
        isString, length = funcs.length,
        f = 0,
        func;

    // go through each function to call back
    for (; f < length; f++) {
        func = funcs[f];
        if (!func) {
            continue;
        }

        // set called with the name of the function on self (this is how this.view works)
        isString = typeof func == "string";
        if (isString && self._set_called) {
            self.called = func;
        }

        // call the function
        cur = (isString ? self[func] : func).apply(self, cur || []);

        // pass the result to the next function (if there is a next function)
        if (f < length - 1) {
            cur = !isArray(cur) || cur._use_call ? [cur] : cur
        }
    }
    return cur;
    }
}
  1. funcs = args.shift(); //this.proxy的第一個參數會是函數名稱字串或函數名稱字串的陣列。取出後剩下的是要傳給這些函數的參數。
  2. return function class_cb(){...} //這邊就是在做Currying: 用另一個函數回傳取代原本的函數。同時將此函數取名class_cb
  3. if (isString && self._set_called) { self.called = func; } 這一段目前看起來只有$.Controller才用的到,先不管他。
  4. 把字串轉成函數self[func]
  5. 重寫return,把一開始傳進來的參數再return給下一個函數用
callback

callback算是蠻常用Class功能,主要用在Ajax回傳結果時,需要連續傳遞給一個或多個以上的函數時使用。
而callback的實作就來自於proxy:

clss.callback = clss[STR_PROTOTYPE].callback = clss[STR_PROTOTYPE].proxy = clss.proxy;
Static 與 Prototype

$.Class中的prototype其實就是John實作的部份,而jmvc又加入對整個Class操作的static scope。
這部份實現蠻長,留給最後一篇$.Class專門討論。

Comments

comments powered by Disqus