【问题标题】:Prototype copy vs Object.create() vs new原型副本 vs Object.create() vs new
【发布时间】:2013-04-19 08:31:07
【问题描述】:

我正在使用继承,我注意到有三种方法可以获得相同的结果。有什么区别?

function Animal(){
}

Animal.prototype.doThat = function() {
    document.write("Doing that");
}

function Bird(){
}

// This makes doThat() visible
Bird.prototype = Object.create(Animal.prototype); // Solution 1
// You can also do:
// Bird.prototype = new Animal(); // Solution 2
// Or:
// Bird.prototype = Animal.prototype; // Solution 3

var myVar = new Bird();
myVar.doThat();

如您所见,我提出了三个解决方案。它们中的每一个都使方法 doThat() 可见。

如果我评论所有这些确实有错误。

如果我只删除其中一个,程序就可以工作。

那么...这三种解决方案的真正区别是什么?

【问题讨论】:

    标签: javascript inheritance


    【解决方案1】:

    Bird.prototype = Animal.prototype; // Solution 3

    由于这会将Animal.prototype 直接分配给Bird.prototype,因此您对后者所做的所有更改也将反映在Animal 以及从它继承的所有其他类中。

    例如:

    Bird.prototype.fly = function() {
        console.log('I can fly.');
    }
    
    var some_animal = new Animal();
    some_animal.fly(); // this will work
    

    这肯定不是你想要的。不仅该子类的每个方法都可用于其他子类,而且如果您覆盖子类中的父方法,最后定义的子类将覆盖其他子类的更改。

    在您的问题标题中,您似乎将此称为“原型副本”。这是不正确的,因为没有创建副本。 Animal.prototypeBird.prototype 将引用同一个对象。


    Bird.prototype = new Animal(); // Solution 2

    长期以来,这一直是设置继承的常用方法,但它有两个主要缺点:

    • 如果Animal 需要参数,你传递哪个?传递随机、无意义的参数只是为了使函数调用正常工作似乎是糟糕的设计。
    • Animal 可能有副作用,例如增加实例计数器,但此时您实际上并没有创建新实例(或不想),您只是想设置继承。

    Bird.prototype = Object.create(Animal.prototype); // Solution 1

    这就是你现在应该这样做的方式 (until a better solution comes)。你真正想做的就是将Animal 的原型连接到Bird 的原型链中。它避免了以前解决方案的所有缺点。

    但要使其正常工作,您还必须在 Bird 构造函数中调用 Animal 构造函数。这就像在其他编程语言中调用super

    function Bird(){
        Animal.call(this);
    }
    

    这确保对新的Animal 实例所做的任何命令/更改都将应用于新的Bird 实例。

    为原型的constructor 属性分配正确的值也是一个好习惯。它通常指原型“所属”的功能,但在上述所有解决方案中,Bird.prototype.constructor 将指代Animal。所以我们必须这样做:

    Bird.prototype.constructor = Bird;
    

    此属性在内部不使用,因此只有在您自己的代码依赖它时,您才必须这样做。但是,如果您创建一个供其他人使用的库,他们可能会期望该属性必须正确值。

    【讨论】:

    • @Rikonator:除非您的代码依赖于它,否则您不必必须。它不在内部使用,因此不是必需的。虽然我认为这是一个很好的做法。会更新。
    • @Rikonator 不,每个对象都有一个构造函数,Bird 的构造函数被固定在 Bird 对象上。当然,由于prototype也是一个对象,所以它也有一个构造函数,你一般不会用到。
    • @ghost23:不,constructor 属性总是从原型继承而来。它不是实例本身的属性。
    • @FelixKling .. for Bird.prototype = Object.create(Animal.prototype); 将影响更改 Animal.prototype 属性。我建议使用 jQuery 扩展 Bird.prototype = jQuery.extend( true, {}, Animal.prototype ); 提供精确的副本
    • @Felix Kling 我对此表示怀疑。举个简单的例子:jsfiddle.net/aJ4fx
    【解决方案2】:

    您可以使用Solution 1Solution 2,但不应该使用Solution 3

    原因是当你做Bird.prototype = Animal.prototype;时,Bird.prototype上的每一个变化也会影响Animal.prototype,因为它们是同一个对象。

    例如,您在Bird.prototype 上附加了一个方法,那么该方法也将适用于Animal,这不是您想要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      • 2012-03-29
      • 1970-01-01
      • 2011-12-16
      • 2010-09-26
      相关资源
      最近更新 更多