Bagaimana cara memanggil metode induk dari kelas anak di javascript?

156

Saya telah menghabiskan beberapa jam terakhir mencoba menemukan solusi untuk masalah saya tetapi tampaknya tidak ada harapan.

Pada dasarnya saya perlu tahu cara memanggil metode induk dari kelas anak. Semua hal yang saya coba sejauh ini berakhir dengan tidak berfungsi atau menulis metode induk secara berlebihan.

Saya menggunakan kode berikut untuk mengatur OOP dalam 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);

Saya perlu memanggil metode induk terlebih dahulu dan kemudian menambahkan beberapa hal lagi di kelas anak.

Dalam sebagian besar bahasa OOP yang akan sesederhana memanggil parent.myMethod() Tapi saya benar-benar tidak dapat memahami bagaimana hal itu dilakukan dalam javascript.

Bantuan apa pun sangat kami hargai, terima kasih!

YemSalat
sumber

Jawaban:

196

Begini caranya: ParentClass.prototype.myMethod();

Atau jika Anda ingin menyebutnya dalam konteks instance saat ini, Anda dapat melakukan: ParentClass.prototype.myMethod.call(this)

Hal yang sama berlaku untuk memanggil metode induk dari kelas anak dengan argumen: ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* Petunjuk: gunakan apply()alih-alih call()meneruskan argumen sebagai array.

YemSalat
sumber
7
Jika Anda ingin menyebutnya dalam konteks instance saat ini, Anda harus melakukan ParentClass.prototype.myMethod.apply() or ParentClass.prototype.myMethod.call () `, seperti yang Anda lakukan dengan konstruktor Anda.
JMM
3
Hanya menambahkan bahwa jika Anda ingin memanggil dengan argumen, mereka masuk ke dalam fungsi apply atau call ( ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
Gershom
Saya tidak mengerti. Jika saya memanggil ParentClass.prototype.myMethod.call (ini); dari ChildObject's myMethod, saya mendapatkan pesan kesalahan "Uncaught TypeError: Tidak dapat membaca properti 'panggilan' yang tidak terdefinisi".
zhekaus
@ Zhekaus, itu berarti Anda tidak memiliki myMethoddi kelas Anda.
YemSalat
2
Saat ini saya menggunakan this.myFun = function () {} untuk mendeklarasikan metode objek, jadi panggil ParentClass.prototype.myFun.call (...) tidak berfungsi, jadi saya harus menggunakan CurrentClass.prototype.myFun.call ( ...). JS adalah ... omong kosong, kita harus menggunakan OOP nyata.
Loenix
156

Gaya ES6 memungkinkan Anda untuk menggunakan fitur baru, seperti superkata kunci. superkata kunci itu semua tentang konteks kelas induk, ketika Anda menggunakan sintaksis kelas ES6. Sebagai contoh yang sangat sederhana, checkout:

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

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

Anda juga dapat menggunakan superuntuk memanggil induk konstruktor:

class Foo {}

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

Dan tentu saja Anda dapat menggunakannya untuk mengakses properti kelas induk super.prop. Jadi, gunakan ES6 dan berbahagialah.

Dmytro Medvid
sumber
10
@ fsinisi90 Saya percaya, pertanyaannya bukan tentang metode kelas induk, melainkan tentang metode instance induk yang tidak dapat dipanggil dengan kata kunci super pada ES6.
mcmlxxxiii
ini juga berfungsi untuk metode yang tidak statis (diuji dengan Chrome, tanpa transpiliing, tidak mencoba kata kunci statis)
Gianluca Casati
Mengapa perlu superdisebut? Apakah ada kesetaraan dalam JS "lama"?
1252748
3
super () harus dipanggil dalam konstruktor kelas anak sebelum hal lain.
user938363
1
@GianlucaCasati: Anda hanya bisa menggunakan super()metode statis; Sepertinya Anda menggunakannya di konstruktor.
ZzZombo
5

Nah untuk melakukan ini, Anda tidak dibatasi dengan Classabstraksi ES6. Mengakses metode prototipe induk konstruktor dimungkinkan melalui __proto__properti (saya cukup yakin akan ada sesama coders JS untuk mengeluh bahwa itu didepresiasi) yang disusutkan tetapi pada saat yang sama menemukan bahwa itu sebenarnya merupakan alat penting untuk kebutuhan sub-classing ( terutama untuk kebutuhan sub-class Array). Jadi sementara __proto__properti masih tersedia di semua mesin JS utama yang saya tahu, ES6 memperkenalkan Object.getPrototypeOf()fungsi di atasnya. The super()alat dalamClass abstraksi adalah gula sintaksis ini.

Jadi jika Anda tidak memiliki akses ke nama induk konstruktor dan tidak ingin menggunakan Classabstraksi Anda masih dapat melakukan sebagai berikut;

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);
    }
}
Redu
sumber
4

Dalam hal tingkat pewarisan berganda, fungsi ini dapat digunakan sebagai metode super () dalam bahasa lain. Ini demo biola , dengan beberapa tes, Anda bisa menggunakannya seperti ini, di dalam metode Anda:call_base(this, 'method_name', arguments);

Itu menggunakan fungsi ES yang sangat baru, kompatibilitas dengan browser yang lebih lama tidak menjamin. Diuji dalam 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);
    }
}
Weeger
sumber
2

Bagaimana dengan sesuatu berdasarkan ide 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
Alessandro
sumber
2

Berikut cara yang bagus untuk objek anak untuk memiliki akses ke properti dan metode induk menggunakan rantai prototipe JavaScript, dan itu kompatibel dengan Internet Explorer. JavaScript mencari rantai prototipe untuk metode dan kami ingin rantai prototipe anak terlihat seperti ini:

Contoh anak -> Prototipe anak (dengan metode Anak) -> Prototipe orang tua (dengan metode Induk) -> Prototipe objek -> null

Metode anak juga dapat memanggil metode induk shadowed, seperti yang ditunjukkan pada tiga tanda bintang di bawah.

Begini caranya:

//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');

CaptureWiz
sumber
1

Ada solusi yang jauh lebih mudah dan lebih kompak untuk pencarian prototipe bertingkat, tetapi membutuhkan Proxydukungan. Penggunaan: SUPER(<instance>).<method>(<args>), misalnya, dengan asumsi dua kelas Adan B extends Adengan metode m: SUPER(new B).m().

function SUPER(instance) {
    return new Proxy(instance, {
        get(target, prop) {
            return Object.getPrototypeOf(Object.getPrototypeOf(target))[prop].bind(target);
        }
    });
}
ZzZombo
sumber
0

Meskipun Anda dapat memanggil metode orangtua dengan prototipe dari orang tua, Anda akan harus lulus contoh anak saat ini untuk menggunakan call, applyatau bindmetode. Itubind Metode akan membuat fungsi baru jadi saya tidak menyarankan bahwa jika Anda peduli untuk kinerja kecuali hanya dipanggil sekali.

Sebagai alternatif, Anda dapat mengganti metode anak dan meletakkan metode induk pada instance saat memanggil metode anak asli.

function proxy(context, parent){
  var proto = parent.prototype;
  var list = Object.getOwnPropertyNames(proto);
  
  var child = {};
  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]){
      child[key] = context[key];
      context[key] = function(){
        context.super = proto[key];
        return child[key].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 this.super(arg) + ", Child "+arg;
  }
}

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

StefansArya
sumber