204

在过去的几个小时里,我一直在试图找到解决问题的方法,但这似乎是无望的。

基本上我需要知道如何从子类调用父方法。到目前为止,我尝试过的所有东西都以无法正常工作或覆盖父方法而告终。

我正在使用以下代码在 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 中完成的。

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

4

9 回答 9

231

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

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

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

于 2012-08-07T22:29:19.310 回答
230

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 并快乐。

于 2016-12-27T13:55:13.540 回答
8

好吧,为了做到这一点,您不受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);
    }
}
于 2017-06-09T16:45:32.733 回答
5

这是子对象使用 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');

于 2019-02-07T21:14:27.043 回答
4

在多继承级别的情况下,该函数可以用作其他语言中的 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);
    }
}
于 2014-06-16T12:11:11.930 回答
2

基于 Douglas Crockford 想法的东西怎么样:

    function Shape(){}

    Shape.prototype.name = 'Shape';

    Shape.prototype.toString = function(){
        return this.constructor.parent
            ? this.constructor.parent.toString() + ',' + this.name
            : this.name;
    };


    function TwoDShape(){}

    var F = function(){};

    F.prototype = Shape.prototype;

    TwoDShape.prototype = new F();

    TwoDShape.prototype.constructor = TwoDShape;

    TwoDShape.parent = Shape.prototype;

    TwoDShape.prototype.name = '2D Shape';


    var my = new TwoDShape();

    console.log(my.toString()); ===> Shape,2D Shape
于 2015-04-14T17:16:32.423 回答
1

对于多级原型查找有一个更简单、更紧凑的解决方案,但它需要Proxy支持。用法:SUPER(<instance>).<method>(<args>)例如,假设有两个类AB extends A方法mSUPER(new B).m()

function SUPER(instance) {
    return new Proxy(instance, {
        get(target, prop) {
            return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
        }
    });
}
于 2018-12-16T13:10:29.227 回答
1

使用经典 js 更灵活的答案。

你定义“_parent = A.prototype;” 在子类中,您可以使用 apply 调用父类的方法:

class A{
    _msg='A';
    _msgOnlyA=' great from A';
    constructor(){
    }
    hello(){
        console.log('hello '+this._msg+', '+this._msgOnlyA);
    }
};
class B extends A{
    _parent = A.prototype;
    _msg='B';
    constructor(){
        super();
    }
    hello(){
        this._parent.hello.apply(this, arguments);
        console.log('hello '+this._msg);
    }
};
var b = new B();
b.hello();

于 2021-05-17T14:04:06.360 回答
0

虽然您可以通过父的原型调用父方法,但您需要传递当前的子实例以使用callapplybind方法。该bind方法将创建一个新函数,因此如果您关心性能,我不建议您这样做,除非它只调用一次。

作为替代方案,您可以替换子方法并将父方法放在实例上,同时调用原始子方法。

function proxy(context, parent){
  var proto = parent.prototype;
  var list = Object.getOwnPropertyNames(proto);
  
  for(var i=0; i < list.length; i++){
    var key = list[i];

    // Create only when child have similar method name
    if(context[key] !== proto[key]){
      let currentMethod = context[key];
      let parentMethod = proto[key];
      
      context[key] = function(){
        context.super = parentMethod;
        return currentMethod.apply(context, arguments);
      }
    }
  }
}

// ========= The usage would be like this ==========

class Parent {
  first = "Home";

  constructor(){
    console.log('Parent created');
  }

  add(arg){
    return this.first + ", Parent "+arg;
  }
}

class Child extends Parent{
  constructor(b){
    super();
    proxy(this, Parent);
    console.log('Child created');
  }

  // Comment this to call method from parent only
  add(arg){
    return super.add(arg) + ", Child "+arg;
  }
}

var family = new Child();
console.log(family.add('B'));

于 2020-03-08T13:18:50.640 回答