javascript - 原型(prototype)复制 vs Object.create() vs new

标签 javascript inheritance

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

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() 可见。

如果我对所有这些都进行评论,那确实是一个错误。

如果我只对其中一个进行反注释,程序就可以运行。

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

最佳答案

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

由于这将 Animal.prototype 直接分配给 Bird.prototype,因此您对后者所做的所有更改也将反射(reflect)在 Animal 中,并且从它继承的所有其他类。

例如:

Bird.prototype.fly = function() {
    console.log('I can fly.');
}

var some_animal = new Animal();
some_animal.fly(); // this will work

这肯定不是你想要的。不仅这个子类的每个方法都可以被其他子类使用,而且如果你在子类中重写父类方法,最后定义的子类将覆盖其他子类的变化。

在您的问题标题中,您似乎将此称为“原型(prototype)副本”。这是不正确的,因为没有创建副本。 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 的原型(prototype)连接到Bird 的原型(prototype)链中。它避免了以前解决方案的所有缺点。

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

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

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

为原型(prototype)的 constructor 属性分配正确的值也是一种很好的做法。它通常是指原型(prototype)“属于”的功能,但在上述所有解决方案中,Bird.prototype.constructor 将指代 Animal。所以我们要做:

Bird.prototype.constructor = Bird;

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

关于javascript - 原型(prototype)复制 vs Object.create() vs new,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41863420/

相关文章:

javascript - 二进制数的 Angular 输入掩码

默认情况下是 Java 类,它将隐式扩展 java.lang.Object

javascript - 为现代基元扩展对象包装器

java - 在继承类的 super 方法中返回 `this`

javascript - 如何实现带有 promise 的渲染逻辑?

javascript - 如何使用 Jquery 检查 2 Contact Form 7 复选框输入是否已选中?

javascript - 同一个 ActiveForm yii2 上的多个模型

javascript - 如何使用 JavaScript 确认对话框菜单触发表单提交事件

Django:查询抽象基类

C++ 继承 - 如何使用指向基类对象的指针访问子方法