[英]Call base method in Javascript using Douglas Crockford's functional inheritance
[英]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());
這確實應該記錄instance
, prototype
, parent
, top
, result
-如“超級”調用所期望的那樣。 這些this.uber("exampleMethod")
調用(在相同實例上使用相同參數調用的相同方法this.uber("exampleMethod")
如何實現此目標? 可怕的雜耍和騙術。
我們看到this.uber
總是調用C.inherits(B)
創建的方法。 B.prototype.uber
無關緊要。 所有調用將使用相同的d
對象(由閉包引用),該對象存儲每個方法名稱的遞歸深度。 p
是C.prototype
,而v
是B.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.