我正在阅读 this article关于 Javascript 中的 super 方法。最下面是作者使用的一种方法,本质上是为每个方法函数对象添加一个name属性,并使用它在当前调用super的对象的原型(prototype)链上找到匹配的方法。
我将复制 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
和原型(prototype)
。
impl
保存调用super
的方法,通过get.caller
获取。 name
是调用 super
的方法的名称(或 undefined
如果 super
不是从方法中调用的). proto
保存调用方法的对象,其原型(prototype)链将被遍历,直到我们找到 super 方法。
现在 foundImpl
让我有点困惑。它是一个 bool 值,最初被赋予 this[name] === impl
的值。由于 this
指向调用方法的对象,this[name]
将返回方法本身,即 ===
到 impl
。每次都是这样,除非 name
是undefined
(我们在方法外调用super
)。
行 while(proto = Object.getPrototypeOf(proto))
然后开始遍历调用对象的原型(prototype)链,从直接父级开始,直到到达 null
.
if(!proto[name])
检查当前原型(prototype)中是否有同名方法。如果不存在,它会跳出循环,如果 foundImpl
为 false,则会抛出错误。如前所述,我能看到这种情况发生的唯一情况是如果 super
在方法外部调用,其中 name
将是 undefined
,因此this[name] === impl
也将为 false。否则,因为 foundImpl
从一开始就已经为真。
else if (proto[name] === impl)
将检查当前具有相同名称的原型(prototype)方法是否严格等于调用 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
的作用到底是什么?
最佳答案
哇,我好久没有想过这篇博文了!我相信大多数常青浏览器在这一点上已经放弃了对 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]
将是来自Middle
的函数Last
,所以 this[name] === impl
将为 false。
然后会继续执行原型(prototype)遍历循环,往上走一步后,我们会发现proto[name] === impl
,所以foundImpl
将设置为 true,我们将再次执行循环并从 Base
正确返回函数。
关于javascript - 了解 Javascript super 方法模拟代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48614202/