对JavaScript原型继承感到困惑

Jus*_*ner 3 javascript inheritance prototype prototypal-inheritance

在" JavaScript权威指南5版 ",第9.2节"原型和继承" 一书中,我找到了以下几个字:

在上一节中,我展示了new运算符创建一个新的空对象,然后调用构造函数作为该对象的方法.然而,这不是完整的故事.创建空对象后,new设置该对象的原型.对象的原型是其构造函数的prototype属性的值.所有函数都有一个prototype属性,在定义函数时会自动创建和初始化.prototype属性的初始值是具有单个属性的对象.此属性名为constructor,并返回与原型关联的构造函数.(您可以回忆一下第7章中的构造函数属性;这就是每个对象都有构造函数属性的原因.)添加到此原型对象的任何属性都将显示为构造函数初始化的对象的属性.

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

Eri*_*ott 15

.constructor诚然,该属性并不重要,并且与从JavaScript中的其他对象继承无关.它只是对象构造函数的方便句柄.

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

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

const car2 = new myCar.constructor();
console.log(car2.constructor); // [Function: Racecar]
Run Code Online (Sandbox Code Playgroud)

重要的是要理解.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]
Run Code Online (Sandbox Code Playgroud)

ES6课程会自动为您完成此操作:

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

const myCar = new Racecar();
console.log(myCar.constructor); // [Function: Racecar]
Run Code Online (Sandbox Code Playgroud)

也就是说,我不是构造函数或ES6类的忠实粉丝,而且我通常对该.constructor属性没什么用处.为什么?因为工厂函数更灵活,更强大,并且它们没有与构造函数和类继承相关的缺陷.请参阅"工厂函数与构造函数与类的比较".


Naw*_*Man 8

比方说,狗是一个哺乳动物.

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

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

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

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

希望这可以帮助.