对JavaScript原型继承感到困惑

时间:2010-02-02 08:26:15

标签: javascript inheritance prototype prototypal-inheritance

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

  

在上一节中,我展示了这一点   new运算符创建一个新的空   对象然后调用构造函数   作为该对象的方法。   这不是完整的故事,   然而。创建空后   对象,新设置的原型   宾语。对象的原型是   。的原型属性的值   它的构造函数。所有   函数有一个原型属性   这是自动创建的   函数初始化时   定义。 的初始值   prototype属性是一个带有的对象   单一财产。这个属性是   命名构造函数并返回   用于构造函数   原型是关联的。 (你   可能会回想起构造函数属性   来自第7章;这就是为什么每一个   object有一个构造函数属性。)   您添加到此的任何属性   原型对象似乎是   初始化的对象的属性   构造函数。

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

4 个答案:

答案 0 :(得分: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属性几乎没用。为什么?因为工厂功能更灵活,更强大,并且它们没有与构造函数和类继承相关的缺陷。请参阅"Factory Functions vs Constructor Functions vs Classes"

答案 1 :(得分:8)

让我们说,狗是一个哺乳动物。

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

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

所以Dog的原型指向哺乳动物的对象。这个Mammal对象有一个对它的构造函数的引用,所以当Dog是新的时,JavaScript看到Dog原型是一个哺乳动物,所以调用Mammal的构造函数来生成一个有效的Mammal对象(另一个)然后使用Dog构造函数使它成为Dog对象。 p>

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

希望这有帮助。

答案 2 :(得分:2)

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

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

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

答案 3 :(得分:1)

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

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

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