Javascript od”)调用—在同一实例上使用相同参数调用相同的方法—实现了这一点?可怕的变戏法和诡计
我们看到Javascript od”)调用—在同一实例上使用相同参数调用相同的方法—实现了这一点?可怕的变戏法和诡计,javascript,Javascript,我们看到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.exam
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
)有自己的(实例)方法时,这是错误的。因此它从p
,而不是从v
中选择方法来调用下一步。它增加递归计数并在实例上调用它
第二个调用来自C.prototype
方法。如果这是第一个调用(在只有prototype方法时通常如此),d.exampleMethod
将是0
。我们再次转到else
分支,但是当没有实例方法时,我们将使比较求值为true并选择v[name]
调用,即我们继承的父方法。它将增加递归计数并调用所选方法
第三个调用将来自B.prototype
方法,而d.exampleMethod
将是1
。这实际上已经在第二个调用中发生了,因为Crockford忘记了在这里说明实例方法。无论如何,它现在转到if
分支,并从v
向上进入原型链,ass如果.constructor
属性在任何地方都已正确设置(继承了它)。它会对存储的次数执行此操作,然后选择要从相应对象调用的下一个方法-A.prototype。在本例中,exampleMethod
计数必须按methodname进行,因为可以尝试从任何调用的超级方法调用不同的方法
至少这一定是个想法,因为很明显,如果有实例方法,计数是完全不可能的。或者当原型链中的对象不拥有相应的方法时,也可能是Crockford试图处理但失败的情况。这太过时了(从ES5开始,您将使用Object.create
而不是new parent()
——我们使用的是ES2017/ES8)虽然你的好奇心是可以理解的,但对我个人来说,这个实现看起来完全是垃圾,而且由于ES6class
语法以及Babel等用于向后兼容的Transpiler的广泛使用,它已经过时了。一旦你理解了这段代码,请在ternet帮个忙,不要试图重复使用它。@Ryan和Patrick Roberts:我只是想理解它,这样我就可以理解我正在读的JS书中的其他一些例子。@kittu:如果你的JS书谈论的是这个函数的实现细节,它可能已经过时了。如果你只需要知道它的功能,那就是继承(在定义构造函数Derived
之后,您可以Derived.inherits(Base)
使Derived
实例具有Base
原型)以及在父类上调用“overrided”方法的方法(ES6和其他语言中的super.foo(x)
将编写为this.uber('foo',x)
)。@Ryan我不理解检查d[name]
的过程,就像if(t){
它是超级过时的(从ES5开始,您将使用对象。创建而不是新父对象()
——我们使用的是ES2017/ES8)虽然你的好奇心是可以理解的,但对我个人来说,这个实现看起来完全是垃圾,而且由于ES6class
语法以及Babel等用于向后兼容的Transpiler的广泛使用,它已经过时了。一旦你理解了这段代码,请在ternet帮个忙,不要试图重复使用它。@Ryan和Patrick Roberts:我只是想理解它,这样我就可以理解我正在读的JS书中的其他一些例子。@kittu:如果你的JS书谈论的是这个函数的实现细节,它可能已经过时了。如果你只需要知道它的功能,那就是继承(在定义构造函数Derived
之后,您可以Derived.inherits(Base)
使Derived
实例具有Base
原型)以及在父类上调用“overrided”方法的方法(ES6和其他语言中的super.foo(x)
将编写为this.uber('foo',x)
)。@Ryan我不懂检查d[name]
,就像if(t){
Wow一样,我们甚至可以让它通过uber
(从示例中删除x
和C.prototype
方法)。至少它不会因为计数而溢出堆栈。如果您想知道超级调用是如何正确完成的,请参阅对此的赋值支持。exampleMethod
在一个原型中定义了exampleMethod
,这就是它的目的…我们非常感谢您的牺牲。哇,我们甚至可以让它进行方法调用通过uber
(从示例中删除x
和C.prototype
方法)。至少它不会由于计数而溢出堆栈。如果您想知道超级调用是如何正确完成的,请参阅该支持
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;
});
// 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;
});
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());