【问题标题】:How to call a parent method from child class in javascript?如何在javascript中从子类调用父方法?
【发布时间】:2012-08-04 23:58:14
【问题描述】:

过去几个小时我一直在试图找到解决问题的方法,但似乎没有希望。

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

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

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

【问题讨论】:

    标签: javascript oop methods parent


    【解决方案1】:

    这是如何完成的:ParentClass.prototype.myMethod();

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

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

    【讨论】:

    • 只需添加,如果您想使用参数调用,它们会进入应用或调用函数 (ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
    • 我不明白。如果我调用 ParentClass.prototype.myMethod.call(this);从 ChildObject 的 myMethod 中,我收到一个错误“未捕获的类型错误:无法读取未定义的属性 'call'”。
    • @zhekaus,这意味着你的班级没有myMethod
    • 我目前正在使用 this.myFun = function() { } 来声明一个对象方法,所以调用 ParentClass.prototype.myFun.call(...) 不起作用,所以我必须使用CurrentClass.prototype.myFun.call(...)。 JS 是……废话,我们应该使用真正的 OOP。
    【解决方案2】:

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

    【讨论】:

    • @fsinisi90 我相信,问题不在于父类的方法,而在于父类的实例方法,从 ES6 开始就不能用 super 关键字调用。
    • 它也适用于非静态方法(用 Chrome 测试,没有转译,没有尝试静态关键字)
    • 为什么需要调用super? “旧” JS 中是否存在等价物?
    • super() 必须先在子类构造函数中调用。
    • @GianlucaCasati:你只能在静态方法中使用super();看起来你在构造函数中使用了它。
    【解决方案3】:

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

    因此,如果您无权访问父构造函数的名称并且不想使用 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);
        }
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        在多继承级别的情况下,该函数可以用作其他语言中的 super() 方法。 Here is a demo fiddle,通过一些测试,你可以像这样使用它,在你的方法中使用: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);
            }
        }
        

        【讨论】:

          【解决方案6】:

          基于 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
          

          【讨论】:

            【解决方案7】:

            有一个更简单、更紧凑的多级原型查找解决方案,但它需要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);
                    }
                });
            }
            

            【讨论】:

              【解决方案8】:

              使用经典 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();

              【讨论】:

              • A.prototype.hello.apply(this, arguments); 如果你想跳过链中的一门课,这就是我想要的;谢谢。但是,对于您的示例,只需调用 super.hello() 会更容易。
              • @Manstie 你在说什么? super() is only valid in derived class constructors
              • @Michael 你是对的,这就是你打电话给super.hello()的原因。
              • @Manstie 哦,很奇怪。当我尝试从非构造函数调用 super 时出现上述错误。不过一定是驾驶舱错误,因为现在我无法重现它。
              【解决方案9】:

              虽然您可以通过父原型调用父方法,但您需要传递当前子实例才能使用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'));

              【讨论】:

              • @Michael 这只是一个替代解决方案,如果您在此页面上滚动,还有另一个可能适合您的用例的答案。
              猜你喜欢
              • 2020-12-27
              • 1970-01-01
              • 1970-01-01
              • 2017-11-09
              • 2012-02-22
              • 1970-01-01
              • 1970-01-01
              • 2014-06-11
              • 1970-01-01
              相关资源
              最近更新 更多