令人困惑的JavaScript继承行为

时间:2010-08-08 23:40:10

标签: javascript oop inheritance prototype constructor

<disclaimer> 
What follows is the fruits of a thought experiment. What I'm doing 
isn't the issue; the symptoms are. Thank you.
</disclaimer>

我终于在JavaScript中围绕构造函数,原型和原型继承。但下面示例中的SomethingSpectactular方法让我感到困惑:

function FinalClass() {    
    return {
        FinalFunction : function() { return "FinalFunction"; },
        TypeName : "FinalClass",
        SomethingSpectacular : function() { 
            return FinalClass.prototype.SubFunction.call(this);
        }
    }
}
FinalClass.prototype = new SubClass();
FinalClass.constructor = FinalClass;

var f = new FinalClass();

这让我感到困惑的原因是:

  1. JavaScript 显然不会像处理属性那样以相同的方式扫描原型链。也就是说,f.SubFunction()会产生错误。
  2. 要获得原型上的方法,每次要执行此操作时,必须至少进行3次点操作。 FinalClass DOT原型DOT Subfunctino DOT调用。你明白了。
  3. 基类(原型)方法没有像我期望的那样出现在Intellisense中。这非常令人讨厌。
  4. 因此,思想体验是确定如果我编写了一个inherits版本,将存根函数插入到为您委托回原型的子类中会发生什么。例如,它会自动创建以下函数并将其添加到FinalClass

    function SubFunction() { SubClass.prototype.SubFunction.call(this); }
    

    现在,我已经完成了所有绘制和工作的内容。 inherits方法是Object.prototypeFunction.prototype的扩展,它以Function为唯一参数。这是基类。通过分析Object.prototype.inherits.caller来确定子类。

    从那里,我在子类上设置prototypeconstructor,然后开始分析子类的新原型上的方法。我在原型和基类的公共接口上构建了一个包含方法的数组。最终结果是一个很好的数组,它包含通过原型或基类构造函数的return语句公开的方法。

    有了这个,我开始在子类上寻找每个方法。如果它不存在,我将它添加到具有相同名称和签名的子类。但是,该方法的主体只是将调用转发给基类。

    现在,我可以逐步完成所有这些代码并且它可以很好地工作,直到我实例化子类的实例。事情变得好运的时候。以下是我使用Visual Studio 2008(SP1)和Internet Explorer 8观察到的内容:

    1. 在实例化之前,BaseClass不公开任何公共方法。这是可以预料的。通过类构造函数的return语句公开的方法在实例化之前不会出现。我很擅长。
    2. 在实例化之前,SubClass公开了来自BaseClass的方法。这正是我的预期。
    3. 一旦我宣布BaseClass的实例,它就拥有了我所期望的所有成员。它有typeNameBaseFunction方法。
    4. 一旦我声明了SubClass的实例,它只有它的构造函数的return语句返回的那些方法。没有来自基类的成员。在继承方法中将基类方法映射到子类的所有工作似乎都已丢失。
    5. 对我来说,最大的谜团是我在执行继承时添加到SubClass的方法的消失。在执行期间,我可以清楚地看到正在修改SubClass,并且正在传播BaseClass的功能。但是,当我创建SubClass的实例时,该信息不再存在。

      我假设这与构造函数,事件顺序或其他我根本看不到的东西有关。

      最后的注释:这个项目的出现是为了理解JavaScript的复杂性及其原型继承系统的工作原理。我知道那里有现有的图书馆。我知道我正在重新发明轮子。我故意重新发明它。有时候,理解事物的最好方法就是自己构建它。这已经是一次巨大的学习经历,但我只是对这一点感到困惑。

      代码

      sti.objects.inherits = function inherits(baseClass) {
      
          var subClass = sti.objects.inherits.caller;
      
          var baseClassName = sti.objects.getTypeName(baseClass);
          var methods = sti.objects.getMethods(baseClass); 
      
          if(!sti.objects.isDefined(baseClass.typeName))
              baseClass.typeName = baseClassName;
      
          var subClass = sti.objects.inherits.caller;
          var subClassName = sti.objects.getTypeName(subClass);
      
          var temp = function() {};
          temp.prototype = new baseClass();
      
          subClass.prototype = temp.prototype;
          subClass.constructor = subClass;
          subClass.typeName = subClassName;
          subClass.baseClass = baseClass.prototype;   //  Shortcut to the prototype's methods
          subClass.base = baseClass;  //  Cache the constructor
      
          for(var index = 0; index < methods.items.length; index++) {
              var resource = methods.items[index].getFunction();
              var methodName = methods.items[index].getName();
              if(methodName != "" && ! sti.objects.isDefined(subClass[methodName])) {
                  var method = sti.objects.createOverride(baseClassName, resource);
                  subClass[methodName] = method;
                  if(typeof subClass.prototype[methodName] == "undefined") {
                      subClass.prototype[methodName] = method;
                  }
              }
          }
      }
      
      Object.prototype.inherits = sti.objects.inherits;
      Function.prototype.inherits = sti.objects.inherits;
      
      function BaseClass() {
          return { 
              A : function A() {return "A";} 
          };
      }
      
      
      function SubClass() {
          inherits(BaseClass);
      
          return {
              B : function B() { return "B"; }
          }    
      }
      
      var b = new BaseClass();
      var s = new SubClass();
      

1 个答案:

答案 0 :(得分:4)

您的构造函数不是构造函数。当您使用new关键字时,Javascript会创建新对象,并期望您的构造函数使用成员等填充它。您的构造函数将返回另一个新对象,与与构造函数用法关联的对象无关,因此原型无法正常工作。

将构造函数更改为

function FinalClass() {
    this.FinalFunction = function() { return "FinalFunction"; };
    this.TypeName = "FinalClass";
    this.SomethingSpectacular = function() { 
        return FinalClass.prototype.SubFunction.call(this);
    };
}

你应该看到预期的继承行为。这是因为在这种情况下,FinalClass方法改变了通过“新”机制创建的上下文对象。您的原始方法是创建另一个不属于FinalClass类型的对象。