我从here
了解了以下示例但我无法理解第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;
});
答案 0 :(得分:4)
我通过以下Douglas Crockford的超级方法示例
哦,你可怜的迷失灵魂。请注意,当语言标准版本仍为ES3时,此函数had its origin in 2002(或更早版本)。今年,我们将看到ES9!
您可以检查Web存档以查看缓慢演变的功能,以处理已发现的所有边缘情况,以及Crockford尝试修复它们。 (请注意,如果其中一个涉及的方法抛出异常,它仍然会失败。)
毋庸置疑,这完全是过时的。 borked 。
有人可以解释一下吗?
我会尝试一下。让我们采取以下代码:
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
- 正如人们可能预期的那样a"超级"呼叫。这些this.uber("exampleMethod")
调用 - 在同一个实例上使用相同的参数调用的相同方法 - 如何实现这一点? 可怕的 jugglery和trickery。
我们看到this.uber
始终调用C.inherits(B)
创建的方法。 B.prototype.uber
无关紧要。所有调用都将使用相同的d
对象(由closure引用),该对象存储每个方法名称的递归深度。 p
为C.prototype
,v
为B.prototype
。
第一个调用来自实例方法(在构造函数中创建)。 d.exampleMethod
仍为0(或刚刚初始化为它,因为它之前不存在),我们转到else
分支以选择下一个要调用的方法。这里检查p[name] == this[name]
,即C.prototype.exampleMethod == x.exampleMethod
,当实例(this
/ x
)具有自己的(实例)方法时,这是假的。因此,它从p
而不是v
中选择方法来调用next。它递增递归计数并在实例上调用它。
第二个电话来自C.prototype
方法。如果这是第一次调用(通常只有原型方法),d.exampleMethod
将是0
。我们再次转到else
分支,但是当没有实例方法时,我们将比较评估为true并选择v[name]
来调用,即我们继承的父方法。它会递增递归计数并调用所选方法。
第三个电话来自B.prototype
方法,d.exampleMethod
来自1
。这实际上已经发生在第二次调用中,因为Crockford忘记在这里考虑实例方法。无论如何,它现在转到if
分支并从v
上升到原型链,假设.constructor
属性在任何地方都已正确设置(inherits
已完成)。对于存储的次数,它会这样做,然后选择下一个要从相应对象调用的方法 - 在我们的情况下为A.prototype.exampleMethod
。
计数必须是per-methodname,因为可以尝试从任何调用的super方法中调用不同的方法。
至少一定是这个想法,显然,如果有实例方法,计数完全关闭。或者当原型链中存在不具有相应方法的对象时 - 也许这是Crockford尝试但未能处理的情况。
答案 1 :(得分:1)
如果您正在尝试了解如何使用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;
});
希望这个解释有点帮助你,但我可能在某些方面有误,因为代码是如此奇怪的实现,并且远非易读。