2012-08-07 71 views
80

我花了最近几个小时试图找到解决我的问题,但它似乎是无望的。如何从JavaScript中的子类调用父方法?

基本上我需要知道如何从子类调用父方法。 迄今为止我尝试过的所有东西都以不工作或覆盖父方法结束。

我使用下面的代码来设置OOP在javascript:

// 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中完成的。

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

回答

123

下面是它如何做:ParentClass.prototype.myMethod();

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

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

+7

如果你想它调用当前实例的情况下,你必须做'ParentClass.prototype.myMethod.apply()或'ParentClass.prototype.myMethod.call()',像你一样与你的构造函数。 – JMM 2012-08-07 23:40:00

+3

只需添加如果你想用参数调用,他们进入应用程序或调用函数('ParentClass.prototype.myMethod.call(this,arg1,arg2,arg3 ...);') – 2015-05-17 11:47:52

+0

我不理解。如果我调用ParentClass.prototype.myMethod.call(this);从ChildObject的myMethod,我有一个错误“未捕获TypeError:无法读取未定义的属性'调用'。 – zhekaus 2016-03-10 20:58:46

2

在多继承级别的情况下,该函数可以用作其他语言的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); 
    } 
} 
0

如何基于道格拉斯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 
36

ES6风格可以让你用新的功能,如super关键字。 super关键字当您使用ES6类语法时,它关于父类上下文。作为一个非常简单的例子,结账:

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

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

此外,您还可以使用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,并开心。

+1

为什么这个答案没有更多upvotes? :) – fsinisi90 2017-05-24 18:07:16

+2

@ fsinisi90我相信,这个问题不是关于父类的方法,而是关于父类的实例方法,它们不能用ES6中的super关键字调用。 – mcmlxxxiii 2017-06-25 20:48:10

+0

它也适用于非静态的方法(使用Chrome进行测试,没有传输,未尝试使用static关键字) – 2018-03-10 13:15:08

0

那么为了做到这一点,您不仅限于使用ES6的Class抽象。通过__proto__属性访问父构造函数的原型方法是可能的(我非常确定会有其他JS编码器投诉它已折旧),这是贬值的,但同时发现它实际上是子分类需求的基本工具(特别是对于Array子分类的需求)。所以虽然__proto__属性在我知道的所有主流JS引擎中仍然可用,但ES6在其上引入了Object.getPrototypeOf()功能。 Class抽象中的super()工具是此语法的一个语法糖。

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