我正在阅读有关Javascript中超级方法的this article。在底部,有一个作者使用的方法,主要涉及向每个方法函数对象添加一个name属性,并使用它来查找当前对象调用super的原型链上的匹配方法。
我将复制下面的code:
var Base = function() {};
// Use the regular Backbone extend, but tag methods with their name
Base.extend = function() {
var Subclass = Backbone.Model.extend.apply(this, arguments);
_.each(Subclass.prototype, function(value, name) {
if (_.isFunction(value)) {
value._methodName = name;
}
});
return Subclass;
};
// Define a special `super` property that locates the super implementation of the caller
Object.defineProperty(Base.prototype, "super", {
get: function get() {
var impl = get.caller,
name = impl._methodName,
foundImpl = this[name] === impl,
proto = this;
while (proto = Object.getPrototypeOf(proto)) {
if (!proto[name]) {
break;
} else if (proto[name] === impl) {
foundImpl = true;
} else if (foundImpl) {
return proto[name];
}
}
if (!foundImpl) throw "`super` may not be called outside a method implementation";
}
});
它使用了我不太熟悉的Underscore.js和Backbone.js,但它们的使用并不是我所怀疑的地方。
设置super
属性getter时,声明了4个变量:impl
,name
,foundImpl
和proto
。
impl
包含通过super
获得的调用get.caller
的方法。 name
是调用super
的方法的名称(如果未在方法中调用undefined
,则为super
)。 proto
保存调用方法的对象,其原型链将被遍历,直到找到super方法。
现在foundImpl
对我来说有点混乱。它是一个布尔值,最初被赋值为this[name] === impl
。由于this
指向从中调用方法的对象,this[name]
将返回方法本身,即===
到impl
。每次都是如此,除非 name
为undefined
(我们在方法外部调用super
)。
然后,行while(proto = Object.getPrototypeOf(proto))
开始迭代调用对象的原型链,从直接父对象开始,直到达到null
。
if(!proto[name])
检查当前原型中是否存在具有相同名称的方法。如果没有,它会突然出现循环,如果foundImpl
为false,则会抛出错误。如前所述,我发现这种情况的唯一情况是,如果在方法外调用super
,则name
将为undefined
,因此this[name] === impl
将为false好。否则,因为foundImpl
从一开始就已经是真的。
else if (proto[name] === impl)
将检查当前具有相同名称的原型方法是否严格等于调用super
的方法。老实说,我不能想到这将是真实的情况,因为从一个方法调用super
它必须被覆盖,使两个不同的函数对象。例如:
var a = { method: function(){ return "Hello!"; } };
var b = Object.create(a);
console.log(a.method === b.method); //true
b.method = function(){ return "Hello World!"; };
console.log(a.method === b.method); //false
毕竟这可能只是一个安全检查,永远不会达到这个条件?
最后,else if (foundImpl)
将检查foundImpl
是否为真(它可能在第一次循环迭代中,除了上面描述的特殊情况)并返回当前的proto[name]
方法如果是的话。
所以我怀疑第二个条件是什么意思:else if (proto[name] === impl)
?它覆盖了什么案例?毕竟foundImpl
的作用是什么?
答案 0 :(得分:1)
arguments.caller
的支持,这使得演示代码有点难以编写,但我会尽力解释
你称之为令人困惑的一句就是foundImpl = this[name] === impl
,它乍看起来总是评估为true
。它需要的原因(实际上我最初对“super
问题”感兴趣的原因)是你有super
次链接的情况。
考虑这样的设置:
const Base = /* ... */;
const First = Base.extend({
sayHello() {
console.log('First#sayHello()');
}
});
const Middle = First.extend({
sayHello() {
this.super();
console.log('Middle#sayHello()');
}
});
const Last = Middle.extend({
sayHello() {
this.super();
console.log('Last#sayHello()');
}
});
如果我致电new Last().sayHello()
,那么super
getter将被调用两次。第一次,正如你所说this[name] === impl
将会立即成为现实。
但是,在第二个调用中,调用者将是来自Middle
的函数,但this[name]
将是来自Last
的函数,因此{{ 1}}将是假的。
执行将继续进入原型遍历循环,在向上一步后,我们会发现this[name] === impl
,因此proto[name] === impl
将设置为true,我们将继续循环再一次并正确地从foundImpl
返回函数。