理解Javascript超级方法仿真代码

时间:2018-02-04 23:55:31

标签: javascript inheritance super

我正在阅读有关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个变量:implnamefoundImplproto

impl包含通过super获得的调用get.caller的方法。 name是调用super的方法的名称(如果未在方法中调用undefined,则为super)。 proto保存调用方法的对象,其原型链将被遍历,直到找到super方法。

现在foundImpl对我来说有点混乱。它是一个布尔值,最初被赋值为this[name] === impl。由于this指向从中调用方法的对象,this[name]将返回方法本身,即===impl。每次都是如此,除非 nameundefined(我们在方法外部调用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的作用是什么?

1 个答案:

答案 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返回函数。