如何从javascript中的子类调用父方法?

IT技术 javascript oop methods parent
2021-01-28 15:18:15

我花了最后几个小时试图找到我的问题的解决方案,但似乎没有希望。

基本上我需要知道如何从子类调用父方法。到目前为止,我尝试过的所有东西最终要么不起作用,要么覆盖了父方法。

我正在使用以下代码在 javascript 中设置 OOP:

// SET UP OOP
// surrogate constructor (empty function)
function surrogateCtor() {}

function extend(base, sub) {
    // copy the prototype from the base to setup inheritance
    surrogateCtor.prototype = base.prototype;
    sub.prototype = new surrogateCtor();
    sub.prototype.constructor = sub;
}

// parent class
function ParentObject(name) {
    this.name = name;
}
// parent's methods
ParentObject.prototype = {
    myMethod: function(arg) {
        this.name = arg;
    }
}

// child
function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
        // HOW DO I CALL THE PARENT METHOD HERE?
        // do stuff
    }
}

// setup the prototype chain
extend(ParentObject, ChildObject);

我需要先调用父类的方法,然后在子类中向它添加更多的东西。

在大多数 OOP 语言中,调用就像调用一样简单parent.myMethod() 但我真的无法理解它是如何在 javascript 中完成的。

非常感谢任何帮助,谢谢!

6个回答

这是它的完成方式: ParentClass.prototype.myMethod();

或者如果你想在当前实例的上下文中调用它,你可以这样做: ParentClass.prototype.myMethod.call(this)

从带有参数的子类调用父方法也是如此: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)*提示:使用apply()而不是call()将参数作为数组传递。

我不明白。如果我调用 ParentClass.prototype.myMethod.call(this); 从 ChildObject 的 myMethod 中,我收到一个错误“未捕获的类型错误:无法读取未定义的属性‘调用’”。
2021-03-22 15:18:15
如果你想在当前实例的上下文中调用它,你必须ParentClass.prototype.myMethod.apply() or 像使用构造函数一样执行ParentClass.prototype.myMethod.call()`。
2021-03-27 15:18:15
我目前正在使用 this.myFun = function() { } 来声明一个对象方法,所以调用 ParentClass.prototype.myFun.call(...) 不起作用,所以我必须使用 CurrentClass.prototype.myFun.call( ……)。JS 是……废话,我们应该使用真正的 OOP。
2021-03-28 15:18:15
@zhekaus,这意味着您没有myMethod上课。
2021-04-01 15:18:15
只需添加,如果您想使用参数调用,它们会进入 apply 或 call 函数(ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);
2021-04-02 15:18:15

ES6 样式允许您使用新功能,例如super关键字。super当您使用 ES6 类语法时,关键字都是关于父类上下文的。作为一个非常简单的例子,结帐:

请记住:我们不能通过super实例方法内的关键字来调用父静态方法调用方法也应该是静态的。

通过实例方法调用静态方法 - TypeError !

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  classMethod() {
    return super.classMethod() + ', too';
  }
}
console.log(Bar.classMethod()); // 'hello' - Invokes inherited static method
console.log((new Bar()).classMethod()); // 'Uncaught TypeError' - Invokes on instance method

通过调用静态方法super- 这有效!

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
  static classMethod() {
    return super.classMethod() + ', too';
  }
}

console.log(Bar.classMethod()); // 'hello, too'

现在super上下文根据调用而变化 - 瞧!

class Foo {
  static classMethod() {
    return 'hello i am static only';
  }

  classMethod() {
    return 'hello there i am an instance ';
  }
}

class Bar extends Foo {
  classMethod() {
    return super.classMethod() + ', too';
  }
}

console.log((new Bar()).classMethod()); // "hello there i am an instance , too"
console.log(Bar.classMethod()); // "hello i am static only"

此外,您可以使用super调用父构造函数:

class Foo {}

class Bar extends Foo {
    constructor(num) {
        let tmp = num * 2; // OK
        this.num = num; // ReferenceError
        super();
        this.num = num; // OK
    }
}

当然,您可以使用它来访问父类属性super.prop所以,使用 ES6 并快乐起来。

它也适用于非静态方法(用 Chrome 测试,没有转译,没有尝试 static 关键字)
2021-03-18 15:18:15
@fsinisi90 我相信,问题不是关于父类的方法,而是关于父类的实例方法,从 ES6 开始,这些方法不能用 super 关键字调用。
2021-03-21 15:18:15
为什么需要super调用它?“旧”JS 中是否有等价物?
2021-03-24 15:18:15
@GianlucaCasati:您只能super()在静态方法中使用看起来你在构造函数中使用了它。
2021-03-27 15:18:15
super() 必须先在子类构造函数中调用。
2021-04-05 15:18:15

为了做到这一点,您不受ClassES6 抽象的限制可以通过__proto__属性访问父构造函数的原型方法(我很确定会有其他 JS 编码员抱怨它已贬值),属性已贬值,但同时发现它实际上是满足子类需求的基本工具(尤其是对于 Array 子分类需要)。因此,虽然该__proto__属性在我知道的所有主要 JS 引擎中仍然可用,但 ES6Object.getPrototypeOf()在其之上引入了该功能。抽象中super()工具Class是它的语法糖。

因此,如果您无权访问父构造函数的名称并且不想使用Class抽象,您仍然可以执行以下操作;

function ChildObject(name) {
    // call the parent's constructor
    ParentObject.call(this, name);
    this.myMethod = function(arg) {
    //this.__proto__.__proto__.myMethod.call(this,arg);
    Object.getPrototypeOf(Object.getPrototypeOf(this)).myMethod.call(this,arg);
    }
}

这是使用 JavaScript 的原型链让子对象访问父属性和方法的好方法,并且它与 Internet Explorer 兼容。JavaScript 在原型链中搜索方法,我们希望子代的原型链看起来像这样:

子实例-> 子的原型(带有子方法)-> 父的原型(带有父方法)-> 对象原型-> null

子方法也可以调用隐藏的父方法,如下面的三个星号 *** 所示。

就是这样:

//Parent constructor
function ParentConstructor(firstName){
    //add parent properties:
    this.parentProperty = firstName;
}

//add 2 Parent methods:
ParentConstructor.prototype.parentMethod = function(argument){
    console.log(
            "Parent says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ParentConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Parent! argument=" + argument);
};

//Child constructor    
function ChildConstructor(firstName, lastName){
    //first add parent's properties
    ParentConstructor.call(this, firstName);

    //now add child's properties:
    this.childProperty = lastName;
}

//insert Parent's methods into Child's prototype chain
var rCopyParentProto = Object.create(ParentConstructor.prototype);
rCopyParentProto.constructor = ChildConstructor;
ChildConstructor.prototype = rCopyParentProto;

//add 2 Child methods:
ChildConstructor.prototype.childMethod = function(argument){
    console.log(
            "Child says: argument=" + argument +
            ", parentProperty=" + this.parentProperty +
            ", childProperty=" + this.childProperty
    );
};

ChildConstructor.prototype.commonMethod = function(argument){
    console.log("Hello from Child! argument=" + argument);

    // *** call Parent's version of common method
    ParentConstructor.prototype.commonMethod(argument);
};

//create an instance of Child
var child_1 = new ChildConstructor('Albert', 'Einstein');

//call Child method
child_1.childMethod('do child method');

//call Parent method
child_1.parentMethod('do parent method');

//call common method
child_1.commonMethod('do common method');

在多继承级别的情况下,此函数可以用作其他语言中的 super() 方法。这是一个演示小提琴,有一些测试,你可以像这样使用它,在你的方法中使用:call_base(this, 'method_name', arguments);

它使用了相当新的 ES 功能,不能保证与旧浏览器的兼容性。在 IE11、FF29、CH35 中测试。

/**
 * Call super method of the given object and method.
 * This function create a temporary variable called "_call_base_reference",
 * to inspect whole inheritance linage. It will be deleted at the end of inspection.
 *
 * Usage : Inside your method use call_base(this, 'method_name', arguments);
 *
 * @param {object} object The owner object of the method and inheritance linage
 * @param {string} method The name of the super method to find.
 * @param {array} args The calls arguments, basically use the "arguments" special variable.
 * @returns {*} The data returned from the super method.
 */
function call_base(object, method, args) {
    // We get base object, first time it will be passed object,
    // but in case of multiple inheritance, it will be instance of parent objects.
    var base = object.hasOwnProperty('_call_base_reference') ? object._call_base_reference : object,
    // We get matching method, from current object,
    // this is a reference to define super method.
            object_current_method = base[method],
    // Temp object wo receive method definition.
            descriptor = null,
    // We define super function after founding current position.
            is_super = false,
    // Contain output data.
            output = null;
    while (base !== undefined) {
        // Get method info
        descriptor = Object.getOwnPropertyDescriptor(base, method);
        if (descriptor !== undefined) {
            // We search for current object method to define inherited part of chain.
            if (descriptor.value === object_current_method) {
                // Further loops will be considered as inherited function.
                is_super = true;
            }
            // We already have found current object method.
            else if (is_super === true) {
                // We need to pass original object to apply() as first argument,
                // this allow to keep original instance definition along all method
                // inheritance. But we also need to save reference to "base" who
                // contain parent class, it will be used into this function startup
                // to begin at the right chain position.
                object._call_base_reference = base;
                // Apply super method.
                output = descriptor.value.apply(object, args);
                // Property have been used into super function if another
                // call_base() is launched. Reference is not useful anymore.
                delete object._call_base_reference;
                // Job is done.
                return output;
            }
        }
        // Iterate to the next parent inherited.
        base = Object.getPrototypeOf(base);
    }
}