【问题标题】:javascript inheritancejavascript继承
【发布时间】:2010-10-30 05:17:54
【问题描述】:

我知道有很多类似的问题对此有很多很好的答案。我试图查看经典的继承方法或那些闭包方法等。不知何故,我认为它们或多或少对我来说是“hack”方法,因为它并不是 javascript 的设计目的。 (如果我错了,欢迎任何人纠正我)。 好的,只要它有效,我对经典的继承模式感到满意,例如:

PARENTClass = function (basevar) { do something here; };
PARENTClass.prototype = { a: b, c: d}; // prototype is auto gen 

// Inheritance goes here
CHILDClass = function (childvar) { do something; };
CHILDClass.prototype = new PARENTClass(*1); // Actual inheritance to the prototype statement
// Instance
CHILDInstance = new CHILDClass(whatever);

以上是我理解的 JS 的继承。但是我不知道如何实现的一种情况是,如果我想在对象创建期间(即在构造函数中)进行一些初始化,并且可以立即使用新对象......我对问题的说明可能不是说得太清楚了,所以让我用下面的 C# Psuedo 来解释我想要做什么:

class PARENT {
    public PARENT (basevar) { ... }
}
class CHILD : PARENT {
    public CHILD (basevar) : PARENT (basevar) // constructor of child, and call parent constructor during construct.
    { ... }
}

出于某种原因(如 init.UI 元素),将它们放在构造函数中似乎是最好的方法。任何人都知道我该怎么做。

PS:在 *1 中,我不知道应该放什么。 PS2:上述情况我DID发现jquery.inherit库可以做到,不知道不使用库能不能实现。 PS3:或者我的理解是错误的。由于 javascript 不是为了模仿 OOP(这就是我称之为 hack 的原因),所以实现这一点的“正确”逻辑是什么。

【问题讨论】:

标签: javascript inheritance


【解决方案1】:

只是想我会提到您要使用的经典模式的一些问题:

  1. 超类上的引用变量将在所有实例上作为本质上的静态变量提供。例如,如果你在 super 中有 var arr = [1,2,3] 并执行 instance_1.arr.push(4) instance_2.arr.push(5) 所有这些实例都会“看到”这些变化。
  2. 所以你解决了 1. 使用 Zakas 称为“构造函数窃取”的 Ayman 解决方案,但现在你调用构造函数两次:一次用于原型,一次用于构造函数窃取。解决方案 - 为您的原型使用像 inheritPrototype 这样的帮助器(我在这篇文章中展示了它的整个实现:inheritPrototype method FWIW,这基本上来自 Zakas 书的第 181 页和一些 Crockford 研究的结合。

    李>
  3. 没有隐私(但话又说回来,您需要使用类似 Durable Object 模式的东西来获得这个,而这可能不是您想要的)

  4. 对象定义“悬空”:解决方案 - 使用 if 语句检查原型的任何函数,然后使用原型字面量定义原型。

我在 github 上有所有这些的运行示例!!!

对我来说,真正深入了解两者同样是一个挑战:Zakas 和 Crockford 关于对象创建和继承的书籍。我还需要尝试一些不同的 JavaScript TDD 框架。所以我决定写一篇关于 TDD 框架和 JavaScript 对象创建和继承的文章。它有运行代码和 jspec 测试!这是链接:* My GitHub Open Source Essay/Book

【讨论】:

    【解决方案2】:

    我有一个轻量级 javascript OOP 包装器,它提供“类类”继承,您可以在其中覆盖基本方法或调用基本构造函数或成员。

    你可以这样定义你的类:

    //Define the 'Cat' class
    function Cat(catType, firstName, lastName)
    {
        //Call the 'Animal' constructor.
        Cat.$baseNew.call(this, firstName, lastName);
    
        this.catType = catType;
    }
    //Extend Animal, and Register the 'Cat' type.
    Cat.extend(Animal, { type: 'Cat' }, {
        hello: function(text)
        {
            return "meaoow: " + text;
        },
        getFullName: function()
        {
            //Call the base 'Animal' getFullName method.
            return this.catType + ": " + Cat.$base.getFullName.call(this);
        }
    })
    
    //It has a built-in type system that lets you do stuff like:
    
    var cat = new Cat("ginger", "kitty", "kat");
    Cat.getType()                     // "Cat"
    cat.getBaseTypesAndSelf()         // ["Cat","Animal","Class"]
    cat.getType()                     // "Cat"
    cat.isTypeOf(Animal.getType())    // "True"
    
    var dynamicCat = Class.createNew("Cat", ["tab","fat","cat"])
    dynamicCat.getBaseTypesAndSelf()  // ["Cat","Animal","Class"]
    dynamicCat.getFullName()          // tab: fat cat
    

    源代码位于:Class.js

    我的博文中还有更多关于OOP in javascript的详细信息

    【讨论】:

      【解决方案3】:

      JavaScript 没有对继承层次结构的内置支持,因为类型扩展应该通过聚合来完成,即,如果要在实例之间共享属性,则直接向对象本身或其原型添加所需的功能。

      尽管如此,JS 足够强大,可以实现其他形式的对象构造,包括经典继承。

      给定一个clone function - 这足以添加“真正的”原型继承,而不是 JavaScript 的混蛋 - 你的示例可以这样实现:

      function ParentClass(baseVar) {
          // do stuff
      }
      
      // don't overwrite the prototype object if you want to keep `constructor`
      // see http://joost.zeekat.nl/constructors-considered-mildly-confusing.html
      ParentClass.prototype.a = 'b';
      ParentClass.prototype.c = 'd';
      
      function ChildClass(childVar) {
          // call the super constructor
          ParentClass.call(this, childVar);
      }
      
      // don't inherit from a ParentClass instance, but the actual prototype
      ChildClass.prototype = clone(ParentClass.prototype);
      ChildClass.prototype.e = 'f';
      

      也可以为基于类的继承添加一些语法糖——我自己的实现can be found here

      上面的例子会这样读

      var ParentClass = Class.extend({
          constructor: function(baseVar) {
              // do stuff
          },
          a: 'b',
          c: 'd'
      });
      
      var ChildClass = ParentClass.extend({
          e: 'f'
      });
      

      【讨论】:

        【解决方案4】:

        这不是 hack 本身; JavaScript 是一种原型语言,由Wikipedia 定义为:

        ..类不存在,行为重用(在基于类的语言中称为继承)是通过克隆作为原型的现有对象的过程来执行的。

        正如它所说,JavaScript 中不使用类;您创建的每个对象都源自 JavaScript Object; JavaScript 中的所有对象都具有prototype 对象,并且您从对象的原型对象创建“继承”方法和属性的所有对象实例。查看 MDC prototype 对象参考以获取更多信息。

        至此,当您拨打电话时:

        CHILDClass.prototype = new PARENTClass();
        

        这允许CHILDClass 对象从PARENTClass 对象向其原型对象添加方法和属性,这产生了类似于基于类的语言中存在的继承思想的效果。由于prototype 对象会影响为该对象创建的每个实例,这允许父对象的方法和属性出现在子对象的每个实例中。

        如果要在子类的构造函数中调用父类的构造函数,请使用 JavaScript call 函数;这允许您在子类的构造函数的上下文中调用父类的构造函数,从而将子类中的新原型属性设置为它们在父类中的设置。

        您也不需要在您指定*1 的地方添加任何内容,因为该行仅用于将方法和属性添加到子类的原型对象;但是请记住,它调用了父类的构造函数,因此如果有任何对父类构造函数的操作至关重要的参数,您应该检查这些参数是否存在以避免 JavaScript 错误。

        【讨论】:

        • 不错且全面的答案。还要添加一件事。例如,在调用父级的构造函数时,将执行一些可能非常昂贵(如从 XML 读取)或用户感知(如调用警报)的东西。即使我在 *1 中什么也没放,基本上还是会被执行;除非我明确地检查父项(例如, if (arguments.length ==0) return; )。这是一个“正确”的解决方案吗?对不起,我是纯OOP出身,还没有真正习惯js的编程逻辑。
        • 是的,我就是这样做的 - 如果有人创建了需要参数的对象实例,那么您无论如何都不想执行这些操作。
        【解决方案5】:

        您可以像这样在子类构造函数中手动调用父构造函数:

        CHILDClass = function (basevar) {
          PARENTClass.call(this, basevar);
          // do something;
        };
        

        这里的技巧是使用call 方法,它允许您在不同对象的上下文中调用方法。详情请见the documentation of call

        【讨论】:

        • 我已经到处搜索了这个功能。我从来没有见过它说得这么简单。我的问题是,这是一种“可接受”的方式吗?如,这会被认为是黑客攻击吗?另外,这是不是像__proto__ 那样被弃用?
        猜你喜欢
        • 1970-01-01
        • 2021-12-07
        • 2011-11-27
        • 2016-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-28
        相关资源
        最近更新 更多