3

在《JavaScript 权威指南 5 版》一书中,第 9.2 节原型和继承中,我找到了以下词语:

在上一节中,我展示了 new 运算符创建一个新的空对象,然后调用构造函数作为该对象的方法。然而,这不是完整的故事。创建空对象后,new 设置该对象的原型。对象的原型是其构造函数的原型属性的值。所有函数都有一个原型属性,该属性在定义函数时自动创建和初始化。原型属性的初始值是具有单个属性的对象。这个属性被命名为constructor,并且指向与原型相关联的构造函数。(你可能还记得第 7 章中的构造函数属性;这就是为什么每个对象都有一个构造函数属性的原因。)你添加到这个原型对象的任何属性都将看起来是由构造函数初始化的对象的属性。

现在,如果这是真的,原型继承怎么可能存在呢?我的意思是,假设构造函数的原型对象最初具有构造函数属性。因为原型对象本身就是一个对象,所以我们经常使用来确定它的构造函数prototype_object.constructor。但是现在本身prototype_object已经有了一个constructor属性,它指向与原型关联的构造函数。在这种情况下,继承怎么可能存在?

4

4 回答 4

15

老实说,该.constructor属性并不重要,并且与从 JavaScript 中的其他对象继承几乎没有关系。它只是对象构造函数的一个方便句柄。

例如,如果你有一个东西的实例,并且你想创建那个东西的另一个实例,但是你没有直接处理它的构造函数,你可以做这样的事情:

const myCar = new Racecar();
console.log(myCar.constructor); // [Function: Racecar]

const car2 = new myCar.constructor();
console.log(car2.constructor); // [Function: Racecar]

了解.constructor对象的属性和类不是同义词很重要。正如您可能已经猜到的那样,该.constructor属性是动态的,就像 JavaScript 中的大多数其他东西一样,因此它不应该用于类型检查之类的任何事情。

同样重要的是要理解该.constructor属性并不意味着某物是其他物的子类。事实上,在 JavaScript 中没有可靠的方法来确定某事物是否是其他事物的子类。因为它是一种动态语言,并且因为有很多方法可以从其他对象继承属性(包括在实例化后从其他对象复制属性),所以 JavaScript 中不像其他语言中那样存在类型安全子类。

了解某物是否兼容类型的最佳方法是对属性进行特性测试。换句话说,鸭式。

instanceof操作员忽略该属性.constructor。相反,它检查构造函数是否.prototype存在于对象的原型链中(通过身份检查)。

使用手动构造函数,继承会混淆.constructor属性连接(使其引用错误的构造函数)。您可以通过手动连接连接来修复它。例如,在 ES5 中这样做的规范方法是:

function Car () {}

console.log(Car.prototype.constructor); // Car

function Racecar () {}

Racecar.prototype = Object.create(Car.prototype);
// To preserve the same relationship we have with the Car
// constructor, we'll need to reassign the .prototype.constructor:
Racecar.prototype.constructor = Racecar;

var myCar = new Racecar();
console.log(myCar.constructor); // [Function: Racecar]

ES6 类会自动为您执行此操作:

// ES6
class Car {}
class Racecar extends Car {}

const myCar = new Racecar();
console.log(myCar.constructor); // [Function: Racecar]

也就是说,我不是构造函数或 ES6 类的忠实粉丝,而且我通常很少使用该.constructor属性。为什么?因为工厂函数更灵活、更强大,而且它们没有与构造函数和类继承相关的陷阱。请参阅“工厂函数与构造函数与类”

于 2016-06-01T21:32:03.267 回答
8

假设,狗是哺乳动物。

function Mammal() {
  this.milk = true;
};

function Dog() { this.bark = true; } Dog.prototype = new Mammal;

所以 Dog 的原型指向了 Mammal 的一个对象。这个 Mammal 对象具有对其构造函数的引用,因此当 Dog 是新的时,JavaScript 会看到 Dog 原型是 Mammal,因此调用 Mammal 的构造函数以生成有效的 Mammal 对象(另一个),然后使用 Dog 构造函数使其成为 Dog 对象。

由此, is 的构造函数Dog.prototype是一个Mammal(添加了额外字段和函数的哺乳动物对象)但 is 的构造Dog函数Dog。继承的存在是因为 Dog 的一个实例有一个 Mammal 作为原型;因此,狗是哺乳动物。当一个方法被调用而 JS 找不到它时Dog.prototype,JS 会查找Mammal.prototype(这是一个添加了额外字段和函数的 Object)。

希望这可以帮助。

于 2010-02-02T08:55:39.790 回答
2

不要担心构造函数属性——它是无关紧要的。跳过这些句子,你可能会更好地理解它。

如果你仍然不确定你的理解,谷歌 __proto__ - JS 对象的内部原型参考。它甚至暴露在 Firefox 和 Safari 上的脚本中。

一个很好的参考是https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/The_Employee_Example/Object_Properties/Inheriting_Properties

于 2010-02-02T12:58:05.257 回答
1

如果你有一个对象obj,它的原型是obj.prototype,而构造函数属性引用obj的构造函数是obj.prototype.constructor

对于对象obj.prototype,情况是一样的。假设proto = obj.prototype,那么对 的构造函数的引用proto将在proto.prototype.constructor

这与 相同obj.prototype.prototype.constructor,因此与 没有冲突obj.prototype.constructor

于 2010-02-02T09:02:51.103 回答