javascript - 了解 Javascript super 方法模拟代码

标签 javascript inheritance super

我正在阅读 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个变量:implnamefoundImpl原型(prototype)

impl保存调用super的方法,通过get.caller获取。 name 是调用 super 的方法的名称(或 undefined 如果 super 不是从方法中调用的). proto 保存调用方法的对象,其原型(prototype)链将被遍历,直到我们找到 super 方法。

现在 foundImpl 让我有点困惑。它是一个 bool 值,最初被赋予 this[name] === impl 的值。由于 this 指向调用方法的对象,this[name] 将返回方法本身,即 ===impl。每次都是这样,除非 nameundefined(我们在方法外调用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/

相关文章:

javascript - 要求还是不要求?

ios - "Ambiguous reference to member ' init(...)"调用基类初始化器

java - C#中 super 关键字的等价物

javascript - CSS 中过渡属性的动态值

javascript - 将表单输入显示为程式化字符串 - Javascript

javascript - 滚动后固定导航栏

返回未定义的 JavaScript 继承示例

javascript通过继承实现私有(private)变量

c# - 你如何覆盖 C# 中的属性?

python - 如何调用父类的实例方法?