了解JavaScript中的原型继承

2020/10/23 21:01 · javascript ·  · 0评论

我是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();

为什么笔者添加DriveFly使用方法prototype,并没有宣布他们的this.Drive内部方法Car类和this.FlySuperCar类?

为什么SuperCar.prototype.constructor需要重新设置为SuperCarconstructor当覆盖的属性prototype设置?我注释了这一行,没有任何改变。

为什么叫Car.call(this, name);SuperCar构造函数?Car当我这样做时不会被“继承”的属性和方法

var myCar = new Car("Car");

这两个块的区别在于,在第一个示例中该实例Drive()仅存在一次,而在第二种方法中Drive()每个实例将存在一次(每次执行new Car()该功能时,drive()都会再次创建该函数)。或不同,第一个使用原型存储函数,第二个使用构造函数。对函数的查找是构造函数,然后是原型。因此,Drive()无论您是在构造函数中还是在原型中,都可以通过查找找到它。使用原型的效率更高,因为通常每个类型只需要一个函数。

newjavascript中调用会自动在原型中设置构造函数。如果要覆盖原型,则必须手动设置构造函数。

javascript中的继承与没什么不同super因此,如果您有子类,则调用超级构造函数的唯一机会就是其名称。

为了增加Norbert Hartl的答案,不需要SuperCar.prototype.constructor,但是有些人将其用作获取对象(在这种情况下为SuperCar对象)的构造函数的便捷方法。

仅从第一个示例开始,Car.call(this,name)就在SuperCar构造函数中,因为执行此操作时:

var mySuperCar = new SuperCar("SuperCar");

这是JavaScript的作用:

  1. 实例化一个新的空白对象。
  2. 新鲜物体的内部原型设置为Car。
  3. SuperCar构造函数运行。
  4. 返回完成的对象并在mySuperCar中进行设置。

请注意,JavaScript是如何没有为您调用Car的。原型照原样,您未在SuperCar中设置的任何属性或方法都将在Car中查找。有时候这很好,例如SuperCar没有Drive方法,但是可以共享Car的Drive方法,因此所有SuperCar都将使用相同的Drive方法。在其他时间,您不想共享,例如每个SuperCar都有其自己的名称。那么如何将每个SuperCar的名称设置为自己的东西呢?您可以在SuperCar构造函数中设置this.Name:

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

这可以,但是请稍等。我们不是在Car构造函数中做完全相同的事情吗?不想重复自己。由于Car已经设置了名称,所以我们就称其为。

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

糟糕,您永远不想更改特殊this对象引用。还记得这四个步骤吗?抓住JavaScript给您的那个对象,因为这是在SuperCar对象和Car之间保留宝贵的内部原型链接的唯一方法。那么,我们如何设置Name而不重复自己,又不扔掉新的SuperCar对象,JavaScript花费了很多精力为我们做准备?

两件事情。一:含义this灵活。二:汽车是一种功能。可以调用Car,而不用原始的,最新的实例化对象,而用SuperCar对象来调用。这给了我们最终的解决方案,这是您问题中第一个示例的一部分:

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

作为一个函数,可以使用该函数的call方法来调用Car ,这会将thisCar内的含义更改为我们正在构建的SuperCar实例。快点!现在,每个SuperCar都有其自己的Name属性。

总结起来,Car.call(this, name)在SuperCar构造函数中,为每个新的SuperCar对象提供了自己唯一的Name属性,但是没有重复Car中已经存在的代码。

一旦您了解了原型,它就不会感到恐惧,但它们根本不像经典的类/继承OOP模型。我写了一篇有关JavaScript原型概念的文章它是为使用JavaScript的游戏引擎编写的,但与Firefox使用的JavaScript引擎相同,因此应该都具有相关性。希望这可以帮助。

诺伯特,您应该注意,第一个示例几乎是道格拉斯·克罗克福德(Douglas Crockford)所说的伪古典继承。需要注意的一些事情:

  1. 您将两次调用Car构造函数,一次是从SuperCar.prototype = new Car()行,另一次是从“构造函数窃取”行Car.call(此...您可以创建一个辅助方法来继承原型,汽车制造商只需运行一次,即可提高设置效率。
  2. SuperCar.prototype.constructor = SuperCar行将允许您使用instanceof来标识构造函数。有些人希望其他人只是避免使用instanceof
  3. 像var arr = ['one','two']这样的引用var在超级上定义时(例如Car)将被所有实例共享。这意味着inst1.arr.push ['three'],inst2.arr.push ['four']等将在所有实例中显示!本质上,您可能不需要静态行为。
  4. 您的第二个块在构造函数中定义fly方法。这意味着每次调用它时,都会创建一个“方法对象”。最好使用原型作为方法!但是,您可以根据需要将其保留在构造函数中-您只需要警惕,因此实际上只初始化一次原型文字(伪):if(SuperCar.prototype.myMethod!='function')...然后定义您的原型文字。
  5. “为什么要调用Car.call(this,name)....”:我没有时间仔细查看您的代码,所以我可能是错的,但这通常是这样,以便每个实例都可以保持自己的状态来修复该问题。我在上面描述了原型链的“静态”行为问题。

最后,我想提一下我在这里可以使用的TDD JavaScript继承代码的一些示例:TDD JavaScript继承代码和随笔我希望得到您的反馈,因为我希望对其进行改进并保持开源。目的是帮助古典程序员快速掌握JavaScript的知识,并补充对Crockford和Zakas书籍的学习。

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

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

这里有几个问题:

您能否解释以下代码块之间的区别?我测试了两个模块。

第一个只创建一个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

什么是最佳做法,为什么?

作者为什么要
使用添加
DriveFly方法prototype,而没有this.DriveCarthis.Fly中将它们声明为方法SuperCar

最好在原型上定义方法,因为:

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

为什么SuperCar.prototype.constructor需要重新设置为SuperCar设置constructor是否覆盖属性prototype我注释了这一行,没有任何改变。

设置constructor时不覆盖属性prototype但是new Car()is的构造函数Car,因此如果设置new Car()SuperCar.prototype,则显然SuperCar.prototype.constructorCar

只要您不重新分配prototype,就不会发生变化:Constructor.prototype.constructor === Constructor例如,对于CarCar.prototype.constructor === Car,但同样真实的ArrayObjectString,...等等。

但是,如果您将另一个对象重新分配给prototype,则不变性将被破坏。通常这不是问题(如您所注意到的),但是最好将其恢复,因为它回答了以下问题:“哪个构造函数在创建新实例时使用此原型对象?”某些代码可能会进行此类检查并依赖于此。请参阅“为什么必须设置原型构造函数?” 对于这种情况。

为什么叫Car.call(this, name);SuperCar构造函数?当我这样做时,不会“继承” Car的属性和方法

var myCar = new Car("Car");

如果你不这样做 Car.call(this, name);那么您的SuperCar实例将没有name属性。当然,您可以决定只做一次this.name = name;,只复制Car构造函数中的代码,但是在更复杂的情况下,重复这样的代码将是不好的做法。

new Car(name)SuperCar构造函数中调用将没有帮助,因为这将创建另一个对象,而您确实需要扩展该this对象。通过不使用newcall而是使用),您实际上告诉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属性设置为问题中的第一个块。

本文地址:http://javascript.askforanswer.com/lejiejavascriptzhongdeyuanxingjicheng.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!