了解JavaScript中的原型继承

wfveoks0  于 2022-12-25  发布在  Java
关注(0)|答案(6)|浏览(140)

我是JavaScript OOP的新手。你能解释一下下面代码块之间的区别吗?我测试过了,两个代码块都能工作。最佳实践是什么?为什么?
第一块:

function Car(name){
    this.Name = name;
}

Car.prototype.Drive = function(){
    console.log("My name is " + this.Name + " and I'm driving.");
}

SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;

function SuperCar(name){
    Car.call(this, name);
}

SuperCar.prototype.Fly = function(){
    console.log("My name is " + this.Name + " and I'm flying!");
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

第二块:

function Car(name){
    this.Name = name;
    this.Drive = function(){ 
        console.log("My name is " + this.Name + " and I'm driving.");
    }
}

SuperCar.prototype = new Car();

function SuperCar(name){
    Car.call(this, name);
    this.Fly = function(){
        console.log("My name is " + this.Name + " and I'm flying!");
    }
}

var myCar = new Car("Car");
myCar.Drive();

var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();

为什么作者使用prototype添加了DriveFly方法,而没有在Car类中声明为this.Drive方法,在SuperCar类中声明为this.Fly
为什么需要将SuperCar.prototype.constructor设置回SuperCar?设置prototype时是否覆盖了constructor属性?我注解掉了此行,没有任何变化。
为什么要在SuperCar构造函数中调用Car.call(this, name);?调用时Car的属性和方法不会被“继承”吗

var myCar = new Car("Car");
pbwdgjma

pbwdgjma1#

为了补充NorbertHartl的答案,SuperCar.prototype.constructor并不是必需的,但是有些人使用它作为获取对象(在本例中是SuperCar对象)的构造函数的一种方便的方式。
从第一个例子开始,Car.call(this,name)就在SuperCar构造函数中,因为当你这样做时:

var mySuperCar = new SuperCar("SuperCar");

这就是JavaScript的作用:
1.示例化一个新的空对象。
1.新对象的内部原型设置为Car。

  1. SuperCar构造函数运行。
    1.返回完成的对象并在mySuperCar中设置。
    注意JavaScript没有为你调用Car。原型是这样的,任何你没有为SuperCar设置的属性或方法都会在Car中查找。有时候这很好,比如SuperCar没有Drive方法,但是它可以共享Car的一个,所以所有的SuperCar都会使用相同的Drive方法。其他时候你不想共享,就像每辆超级跑车都有自己的名字一样,那么如何将每辆超级跑车的名字设置为它自己的东西呢?你可以在超级跑车构造函数中设置这个。
function SuperCar(name){
    this.Name = name;
}

这是可行的,但是等一下。我们不是在Car构造函数中做了完全相同的事情吗?不想重复我们自己。既然Car已经设置了名称,我们就调用它吧。

function SuperCar(name){
    this = Car(name);
}

哎呀,你永远不想改变特殊的this对象引用。还记得这4个步骤吗?坚持JavaScript给你的那个对象,因为这是保持SuperCar对象和Car之间宝贵的内部原型链接的唯一方法。那么,我们如何设置Name,而不重复自己的操作,也不丢弃JavaScript花了这么多精力为我们准备的新SuperCar对象呢?
两件事第一this的含义是灵活的。二:Car是一个函数,可以调用Car,但不是用一个原始的、新示例化的对象,而是用一个SuperCar对象,这就给出了最终的解决方案,也就是你问题中第一个例子的一部分:

function SuperCar(name){
    Car.call(this, name);
}

作为一个函数,Car可以被函数的call method调用,这将Car中this的含义改变为我们正在构建的SuperCar示例。现在,每个SuperCar都有了自己的Name属性。
最后,SuperCar构造函数中的Car.call(this, name)为每个新的SuperCar对象赋予其唯一的Name属性,但不复制Car中已有的代码。
一旦你理解了原型,你就不会害怕它们了,但是它们根本不像经典的类/继承OOP模型。我写过一篇关于the prototypes concept in JavaScript的文章。它是为一个使用JavaScript的游戏引擎写的,但是它和Firefox使用的是同一个JavaScript引擎,所以应该都是相关的。

w46czmvw

w46czmvw2#

这两个块的不同之处在于,在第一示例中Drive()将仅存在一次,而在第二方法中,Drive()将针对每个示例而存在(每次你做new Car()函数drive()将再次创建).或者不同的说,第一次使用原型来存储函数,第二次使用构造函数.函数的查找是构造函数,然后是原型.所以对于Drive()的查找,不管它是在构造函数中还是在原型中,使用原型会更有效,因为通常每个类型只需要一个函数。
javascript中的new调用会自动设置原型中的构造函数,如果你要覆盖原型,那么你必须手动设置构造函数。
javascript中的继承和super完全不同,所以如果你有一个子类,调用超构造函数的唯一机会就是它的名字。

4szc88ey

4szc88ey3#

诺伯特,你应该注意到你的第一个例子和道格拉斯·克罗克福德所说的伪经典继承差不多。
1.您将调用Car构造函数两次,一次来自SuperCar.prototype = new Car()行,另一次来自“constructor stealing”行Car.call(这......您可以创建一个helper方法来继承原型,并且您的Car构造函数只需运行一次,从而使设置更高效。

  1. constructor = SuperCar行将允许您使用instanceof来标识构造函数。有些人希望这样做,有些人则避免使用instanceof
    1.参考变量如下:var arr =['one','two']当在超级对象(例如Car)上定义时,将被所有示例共享。这意味着inst1.arr.push ['three'],inst2.arr.push ['four']等将在所有示例中显示!本质上,这是您可能不想要的静态行为。
    1.第二个代码块定义了构造函数中的fly方法。这意味着每次调用它时,都会创建一个“方法对象”。最好使用方法的原型!不过,如果愿意,你可以将其保留在构造函数中--你只需要保护它,这样你实际上只初始化原型字面量一次(伪):if(SuperCar.prototype.myMethod!= 'function')...然后定义你的原型文字。
    1.“为什么叫Car.call(这个,名字)......”:我没有时间仔细看你的代码,所以我可能是错的,但这通常是为了让每个示例都能保持自己的状态,以修复我上面描述的原型链接的“静态”行为问题。
    最后,我想提一下,我有几个TDD JavaScript继承代码的例子在这里工作:TDD JavaScript Inheritance Code and Essay我希望能得到你们的反馈,因为我希望能改进它并保持它的开源性。我的目标是帮助传统程序员快速掌握JavaScript,同时也是对Crockford和Zakas书籍的补充。
lnlaulya

lnlaulya4#

我不能100%肯定,但我相信区别在于第二个示例只是将Car类的内容复制到SuperCar对象中,而第一个示例将SuperCar原型链接到Car类,因此对Car类的运行时更改也会影响SuperCar类。

bzzcjhmw

bzzcjhmw5#

function abc() {
}

为函数abc创建的原型方法和属性

abc.prototype.testProperty = 'Hi, I am prototype property';
abc.prototype.testMethod = function() { 
   alert('Hi i am prototype method')
}

为函数abc创建新示例

var objx = new abc();

console.log(objx.testProperty); // will display Hi, I am prototype property
objx.testMethod();// alert Hi i am prototype method

var objy = new abc();

console.log(objy.testProperty); //will display Hi, I am prototype property
objy.testProperty = Hi, I am over-ridden prototype property

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html

e5njpo68

e5njpo686#

这里有几个问题:
你能解释一下下面代码块之间的区别吗?我测试过了,两个块都能工作。
第一个函数只创建一个Drive函数,第二个函数创建其中的两个:一个在myCar上,另一个在mySuperCar上。
以下代码在执行第一个或第二个块时会给予不同的结果:

myCar.Fly === mySuperCar.Fly // true only in the first case
Object.keys(myCar).includes("Fly") // true only in the second case
Object.keys(Car.prototype).length === 0 // true only in the second case

最佳实践是什么?为什么?
为什么作者使用prototype添加了DriveFly方法,但没有在Car类中声明为this.Drive方法,在SuperCar类中声明为this.Fly
最好在原型上定义方法,因为:

  • 每个方法仅定义一次
  • 每个方法也可用于创建时没有执行构造函数的示例(调用Object.create(Car.prototype)时就是这种情况);
  • 您可以检查某个方法是在示例的原型链中的哪一层定义的。

为什么SuperCar.prototype.constructor需要设置回SuperCar?设置prototypeconstructor属性被覆盖了吗?我注解掉了此行,没有任何变化。
prototype被设置时,constructor属性不会被覆盖,但是new Car()的构造函数是Car,所以如果你把new Car()设置为SuperCar.prototype,那么SuperCar.prototype.constructor显然就是Car
只要不重新分配给prototype,就存在不变性:Constructor.prototype.constructor === Constructor。例如,对于Car,这是正确的:但是对于ArrayObjectString等也同样如此。
但是如果你给prototype重新分配了一个不同的对象,这个不变性就被破坏了。通常这不是一个问题(正如你所注意到的),但是最好恢复它,因为它回答了问题 “当创建新示例时哪个构造函数使用这个原型对象?" 一些代码可能会做这样的检查并依赖它。
为什么要在SuperCar构造函数中调用Car.call(this, name);?调用时Car的属性和方法不会被“继承”吗

var myCar = new Car("Car");

如果你不执行Car.call(this, name);,那么你的SuperCar示例就没有name属性,当然你也可以只执行this.name = name;,它只是复制了Car构造函数中的代码,但是在更复杂的情况下,这样的代码重复是不好的。
SuperCar构造函数中调用new Car(name)是没有帮助的,因为这将创建 * 另一个 * 对象,而实际上您需要扩展this对象。(改为使用call)实际上是告诉Car函数不要作为构造函数运行(例如,* 不 * 创建新对象),而是使用传递给它的对象。

时代变了

在JavaScript的现代版本中,您可以使用super(name)代替Car.call(this, name)

function SuperCar(name) {
    super(name);
}

今天,您还将使用class语法,并编写问题的第一个代码块,如下所示:

class Car {
    constructor(name) {
        this.name = name;
    }
    drive() {
        console.log(`My name is ${this.name} and I'm driving.`);
    }
}

class SuperCar extends Car {
    constructor(name) {
        super(name);
    }
    fly() {
        console.log(`My name is ${this.name} and I'm flying!`);
    }
}

const myCar = new Car("Car");
myCar.drive();

const mySuperCar = new SuperCar("SuperCar");
mySuperCar.drive();
mySuperCar.fly();

请注意,您甚至不必提及prototype属性就可以实现此目标,class ... extends语法还负责设置prototype.constructor属性,就像您的问题中的第一个块所做的那样。

相关问题