簡體   English   中英

有人可以解釋道格拉斯·克羅克福德的超級方法嗎?

[英]Can someone please explain Douglas Crockford's uber method?

我從這里通過以下示例

但是我無法從LINE 1中了解:

Function.method('inherits', function(parent){
    this.prototype = new parent();
    var d = {},
    p = this.prototype;
    this.prototype.constructor = parent;
    this.method('uber', function uber(name){   //LINE 1
        if(!(name in d)){
            d[name] = 0;
        }

        var f, r, t = d[name], v = parent.prototype;
        if(t){
            while(t){
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name];
        } else {
            f = p[name];
            if(f == this[name]){
                f = v[name];
            }
        }
        d[name] +=1;
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        d[name] -= 1;
        return r;
    });
    return this;
});

我經歷了道格拉斯·克羅克福德(Douglas Crockford)的uber方法的以下示例

哦,你可憐的迷魂。 請注意,此功能起源於2002年或更早的版本,當時語言標准版本仍為ES3。 今年,我們將看到ES9!

您可以檢查Web存檔,以查看該功能在逐步發展以處理所有發現的邊緣情況,而Crockford試圖對其進行修復。 (請注意,如果其中一種涉及的方法引發異常,它仍然會嚴重失敗)。

不用說,這完全過時了 感到無聊

有人可以解釋一下嗎?

我會試一試。 讓我們采用以下代碼:

function A() { }
A.prototype.exampleMethod = function() {
    console.log("top");
    return "result";
};
function B() { }
B.inherits(A);
B.prototype.exampleMethod = function() {
    console.log("parent");
    return this.uber("exampleMethod");
};
function C() {
    this.exampleMethod = function() {
        console.log("instance");
        return this.uber("exampleMethod");
    }
}
C.inherits(B);
C.prototype.exampleMethod = function() {
    console.log("prototype");
    return this.uber("exampleMethod");
};

var x = new C();
console.log(x.exampleMethod());

確實應該記錄instanceprototypeparenttopresult -如“超級”調用所期望的那樣。 這些this.uber("exampleMethod")調用(在相同實例上使用相同參數調用的相同方法this.uber("exampleMethod")如何實現此目標? 可怕的雜耍和騙術。

我們看到this.uber總是調用C.inherits(B)創建的方法。 B.prototype.uber無關緊要。 所有調用將使用相同的d對象(由閉包引用),該對象存儲每個方法名稱的遞歸深度。 pC.prototype ,而vB.prototype

第一次調用來自實例方法(在構造函數中創建)。 d.exampleMethod仍為0(或因為之前不存在而被初始化),然后轉到else分支以選擇下一個要調用的方法。 在這里它檢查p[name] == this[name] ,即C.prototype.exampleMethod == x.exampleMethod ,當實例( this / x )具有自己的(instance)方法時為false。 因此,它從p選擇方法,而不是從v選擇方法以進行下一步調用。 它增加遞歸計數並在實例上調用它。

第二個調用來自C.prototype方法。 如果是第一次調用(通常只有原型方法,則此調用), d.exampleMethod將為0 再次,我們將轉到else分支,但是當沒有實例方法時,我們將比較評估為true並選擇v[name]進行調用,即我們繼承的父方法。 它將增加遞歸計數並調用選定的方法。

第三次調用將來自B.prototype方法,而d.exampleMethod將為1 實際上,這在第二個調用中已經發生,因為Crockford忘記在此處考慮實例方法。 無論如何,它現在假定進入了if分支,並從v上升到原型鏈,假設.constructor屬性在任何地方.constructor正確設置( inherits做到了)。 這樣做會存儲的次數,然后從相應的對象中選擇下一個要調用的方法-在本例中為A.prototype.exampleMethod

計數必須是按方法名的,因為一個人可能試圖調用與任何調用的超級方法不同的方法。

至少一定是這個主意,因為很明顯,如果有實例方法,則計數將全部取消。 或者,當原型鏈中有一些對象不擁有各自的方法時-也許也是Crockford嘗試但未能處理的情況。

如果您想了解如何使用ES5來實現類以及如何實現類的繼承,那么來自Douglas Crockford的代碼示例有點過時了,只是一團糟。 這使得初學者很難理解它。 (此外,他的解釋缺乏很多細節,也無濟於事)

在我看來,關於如何使用ES5實現類模式的一個更好的示例是: http : //arjanvandergaag.nl/blog/javascript-class-pattern.html

但毫無疑問,讓我們一步步走下去:

// He extended the "prototype" of the Function object to have some syntactic sugar
// for extending prototypes with new methods (a method called 'method').

// This line extends the prototype of the Function object by a method called 'inherits' using the syntactic sugar method mentioned above.
Function.method('inherits', function(parent){
    /** 'this' is a reference to the Function the 'inherits' method is called 
     * on, for example lets asume you defined a function called 'Apple'
     * and call the method 'inherits' on it, 'this' would be a reference of 'Apple'-Function object.
    **/

    /**
     * Hes extending the prototype of the base function by the prototype of
     * the 'parent' function (the only argument the 'inherits' method takes),
     * by creating a new instance of it.
    **/
    this.prototype = new parent();
    // BAD VARIABLE NAMING!!!
    var d = {}, // variable to create a dictionary for faster lookup later on.
    p = this.prototype; // save the prototype of the base function into variable with a short name
    this.prototype.constructor = parent; // set the constructor of the base function to be the parent.


    /**
     * Extend the base function's prototype by a method called 'uber',
     * it will nearly the same function as the 'super' keyword in OO-languages,
     * but only to call methods of the parent class.
     **/
    this.method('uber', function uber(name){

        if(!(name in d)){
            // if the value name doesn't exist in the dictionary
            d[name] = 0; // set the key to the value of name and the value to 0
        }

        // BAD VARIABLE NAMING AGAIN!!!
        var f, r, t = d[name], v = parent.prototype;
        // f is used to store the method to call later on.
        // t is the value of the key inside the 'd' dictionary which indicates the depth to go up the prototype tree
        // v is the parent functions prototype
        // r is the result the method 'f' yields later on.

        // check if the attribute 'name' exists in the dicionary.
        // because if it doesn't exist t will be 0 which resolves to false.
        if(t){
            // the loop is used to walk the tree prototype tree until the implementation with the depth of t is found. 
            while(t){
                v = v.constructor.prototype;
                t -= 1;
            }
            f = v[name]; // set f to the method name of the t-th parent protoype 
        } else {
            // if the attibute 'name' doesn't exist inside the dictionary
            f = p[name]; // use the method 'name' of the base class prototype.
            if(f == this[name]){
                // if the method 'name' is a member of the base class 
                f = v[name]; // use the method 'name' of the parent prototype instead.
            }
        }

        // increment the corresponding dictionary value for the depth of the 'uber' call.
        d[name] +=1;
        // call the method saved to 'f' in context of the base class and apply the 'arguments' array to it and save the result to 'r'.
        r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
        // decrement the corresponding dictionary value for the depth of the 'uber' call.
        d[name] -= 1;
        // return the result
        return r;
    });
    return this;
});

希望這種解釋能對您有所幫助,但是我在某些方面可能是錯的,因為代碼是如此奇怪的實現,並且距離易讀性還很遠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM