【问题标题】:Javascript prototypal inheritance: problem when parent and child receive same object as argument?Javascript原型继承:当父母和孩子收到相同的对象作为参数时的问题?
【发布时间】:2011-07-31 14:57:54
【问题描述】:

我试图让原型继承以下列方式工作:

// Parent constructor
function Parent(obj) {
    this.id = obj.id || 0;
    this.name = obj.name || "";
};

// Child constructor
function Child(obj) {
    Parent.call(this,obj);
    this.type = obj.type || "";
}

Child.prototype = new Parent;

似乎教科书......但将obj 传递给父母和孩子似乎会导致问题; Parent 表示当孩子试图通过 Child.prototype = new Parent; 进行原型制作时,obj 未定义。我可以解决这个问题的唯一方法是使用这个丑陋的黑客:

// 'Hacked' Parent constructor
function Parent(obj) {
    if (obj) {
        this.id = obj.id || 0;
        this.name = obj.name || "";
    }
};

当然有更好的方法,但我在任何地方都找不到答案。请帮忙!!

【问题讨论】:

    标签: javascript inheritance prototype constructor


    【解决方案1】:

    Child.prototype = new Parent; 创建一个不带任何参数的新 Parent 并返回其原型,并将其分配给 Child.prototype。这种方法效果很好,但问题是它只适用于参数为 0 的父母(new Parent 在这种情况下相当于new Parent())。

    对于带参数的父构造函数,我建议定义一个继承函数来处理你的继承。

    我之前提出的修复是Child.prototype = Parent.prototype;。这会起作用,但现在Child.prototypeParent.prototype引用,而不是对象。换句话说,如果您将方法 hello 添加到 Child,则 Parent 也会收到该方法。糟糕的时代!

    这个问题的最佳解决方案是定义一些inherits函数:

    function inherit(parentPrototype) {
      function F() {};
      F.prototype = parentPrototype;
      return new F;
    }
    

    我们在这里所做的与仅将 Parent.prototype 分配给 Child 略有不同。我们正在创建一个具有父原型的新函数,并返回函数 F 的新实例因此,当您向 Child 添加方法时,实际上是在将它们添加到F函数。

    现在要创建 Child 对象,您可以:

    function Child() {};
    Child.prototype = inherit(Parent.prototype); // returns an instance of F
    

    然后您可以在不影响父级的情况下向子级添加方法。

    【讨论】:

    • 一个很好的答案 - 谢谢:) 澄清一点(与您的 hello() 方法示例有关)......在我的真实案例中,Parent 具有我显然希望 Child 继承的方法。由于不再使用new,我需要做一些特别的事情来启用它吗? (例如手动分配this,手动将方法原型分配给Child等)再次感谢!
    • Child.prototype = Parent; 只会添加一个函数作为原型对象,其原型不会被检查。但是,如果您直接在 Parent 构造函数本身上添加方法,则子对象可以访问它们,是的,但是 Parent 本身不起作用,所以在我看来,这并不是真正的继承。
    • 不,Parent 是对一个函数的引用,该函数的原型具有所有声明的 Parent 方法。当您将Child 原型设置为Parent 的原型时,它会获得所有这些方法。然后,您可以使用特定于 Child 的方法添加到 Child 原型。
    • 我所指的问题是在您将其原型分配给Parent 之前设置任何特定于子级的方法。 Child.prototype = Parent 就像任何其他任务一样;你失去了之前在Child.prototype中持有的价值。
    • 我原来的答案其实有一个小错误,请重新阅读帖子,因为我已经更正了。
    【解决方案2】:

    看起来更愉快一点:

    function Parent(obj) {
        if (!obj) {return;}
        this.id = obj.id || 0;
        this.name = obj.name || "";
    }
    

    您的继承风格更符合“经典”模式。

    你也可以这样做:

    Child.prototype = Parent.prototype;
    

    ...但是任何后续添加到 Child.prototype 的内容也将最终出现在 Parent 原型上(反之亦然)。

    也许这就是为什么这种行为经常被包装成在需要时直接返回对象,以一种你不能轻易覆盖父对象的方式(尽管你可以通过 obj.constructor.prototype):

    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
    

    【讨论】:

    • 感谢您的回答。我在 Crockford 的网站上看到了这个解决方案,但并没有完全理解它;我现在可以清楚地看到好处了!
    • 另一件事:如果您不想打扰类型检查或实用方法,您可以简单地传入一个空对象:Child.prototype = new Parent({}); 此外,您可以修改或制作新版本我和 Emmerich 给出的函数允许使用更简单的语法:Child.prototype = inherit(Parent);
    • 谢谢 - 我已经使用了你们建议的继承/创建方法,它运行良好。我需要实用方法,所以空对象不起作用,但还是不错的简单想法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 2020-12-04
    • 1970-01-01
    相关资源
    最近更新 更多