在“ JavaScript权威指南5版”一书的第9.2节“原型与继承”中,我找到了以下词语:
在上一节中,我展示了这一点 new运算符创建一个新的空 对象然后调用构造函数 作为该对象的方法。 这不是完整的故事, 然而。创建空后 对象,新设置的原型 宾语。对象的原型是 。的原型属性的值 它的构造函数。所有 函数有一个原型属性 这是自动创建的 函数初始化时 定义。 的初始值 prototype属性是一个带有的对象 单一财产。这个属性是 命名构造函数并返回 用于构造函数 原型是关联的。 (你 可能会回想起构造函数属性 来自第7章;这就是为什么每一个 object有一个构造函数属性。) 您添加到此的任何属性 原型对象似乎是 初始化的对象的属性 构造函数。
现在,如果确实如此,原型继承如何存在?我的意思是,假设构造函数的原型对象最初具有构造函数属性。因为原型对象本身是一个对象,为了确定它的构造函数,我们经常使用prototype_object.constructor
。但现在prototype_object
已经拥有constructor
属性,它指向 与原型相关联的构造函数 。在这种情况下,如何存在继承?
答案 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上的脚本中。
答案 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
没有冲突。