从继承类的函数调用父函数

call parent ctor from ctor of inheriting class

本文关键字:函数 函数调用 继承      更新时间:2023-09-26

是否可以不这样做

function Animal(name, numLegs) {
    this.name = name;
    this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
    console.log("Hi my name is " + this.name);
};
function Penguin(name) {
    this.name = name;
    this.numLegs = 2;
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();

呢?

function Animal(name, numLegs) {
    this.name = name;
    this.numLegs = numLegs;
}
Animal.prototype.sayName = function() {
    console.log("Hi my name is " + this.name);
};
function Penguin(name) {
    return new Animal(name, 2);
}
Penguin.prototype = new Animal();
var penguin = new Penguin("Tux");
penguin.sayName();

我发现第二个版本更优雅,并希望两个版本的结果相同,但对于第二个版本,codeacademy告诉我

Oops, try again. Make sure to create a new Penguin instance called penguin!

这不是调用父类构造函数的正确方法:

function Penguin(name) {
    return new Animal(name, 2);
}

正确的方法如下:

function Penguin(name) {
    Animal.call(this, name, 2);
}

原因在于new的工作方式:

  1. 假设您有一个名为ABC的函数。
  2. 当你执行new ABC时,JavaScript创建一个ABC.prototype的实例,并将其绑定到ABC函数中的this,这就是为什么你可以在ABC中添加this的属性。
  3. 构造函数默认返回this ,除非你显式返回另一个对象。

Codecademy抱怨你的代码的原因是因为你返回的new Animal(name, 2)而不是instanceof Penguin

正如我之前所说的,调用父构造函数的正确方法是使用ParentConstructor.call(this, arg1, arg2, ...)。在本例中,我们将父构造函数中的this设置为与当前构造函数(即由new创建的实例)中的this相同的值。


如果你想写出优雅的代码,那么试试size:

function defclass(prototype) {
    var constructor = prototype.constructor;
    var instance = prototype.instance = function () {};
    constructor.prototype = instance.prototype = prototype;
    return constructor;
}
function extend(parent, keys) {
    var supertype = keys.super = parent.prototype;
    var prototype = new supertype.instance;
    for (var key in keys) prototype[key] = keys[key];
    return defclass(prototype);
}

使用defclassextend你可以重写你的代码如下:

var Animal = defclass({
    constructor: function (name, numLegs) {
        this.name = name;
        this.numLegs = numLegs;
    },
    sayName: function () {
        console.log("Hi my name is " + this.name);
    }
});
var Penguin = extend(Animal, {
    constructor: function (name) {
        this.super.constructor.call(this, name, 2);
    }
});
var penguin = new Penguin("Tux");
penguin.sayName();

多酷啊?

我认为区别在于构造函数不返回值。所以如果你调用

new Penguin('bla')

不是企鹅函数返回新对象,而是new返回新对象。因此,如果您让Penguin()返回一个新对象,这将与new-keyword冲突。

如果你想调用父构造函数,你可以这样做:

function Penguin(name) {
   Animal.call(this, name, 2);
}

还有一点:当你把Animal的原型赋值给它的子原型Penguin时,你在你的例子中调用了不带参数的函数。有一种更简洁的方法:

Penguin.prototype = Object.create(Animal.prototype);

之后,您已经丢失了Penguin的构造函数,因此您需要像这样重新分配它:

Penguin.prototype.constructor = Animal;

详细说明如下:https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript

和https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript

在第二个例子中,您返回的不是Penguin的实例,而是Animal的实例。如果你想给企鹅添加更多的功能,你需要用额外的功能来装饰Animal类。

function Penguin(name) {
    var self = new Animal(name, 2);
    self.penguinFunction = function (){
        //do something here
    }
    return self;
}