자바 스크립트의 자식 클래스에서 부모 메서드를 호출하는 방법은 무엇입니까?


156

지난 몇 시간 동안 내 문제에 대한 해결책을 찾으려고 노력했지만 희망이없는 것 같습니다.

기본적으로 자식 클래스에서 부모 메서드를 호출하는 방법을 알아야합니다. 지금까지 시도한 모든 것들이 작동하지 않거나 부모 메서드를 덮어 쓰게됩니다.

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에서 어떻게 수행되는지 알 수 없습니다.

어떤 도움이라도 대단히 감사합니다, 감사합니다!

답변:


196

그 방법은 다음과 같습니다. ParentClass.prototype.myMethod();

또는 현재 인스턴스의 컨텍스트에서 호출하려는 경우 다음을 수행 할 수 있습니다. ParentClass.prototype.myMethod.call(this)

인수를 사용하여 자식 클래스에서 부모 메서드를 호출하는 경우에도 마찬가지입니다. ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)* 힌트 : 인수를 배열로 전달 하는 apply()대신 사용하십시오 call().


7
현재 인스턴스의 컨텍스트에서 호출 ParentClass.prototype.myMethod.apply() or 하려면 생성자와 마찬가지로 ParentClass.prototype.myMethod.call ()`을 수행해야합니다.
JMM

3
(가) 적용하거나 통화 기능 (내부 그냥 당신이 인수를 호출 할 경우, 그들이가는 것을에서 추가 ParentClass.prototype.myMethod.call(this, arg1, arg2, arg3...);)
게르 솜

이해가 안 돼요 ParentClass.prototype.myMethod.call (this);를 호출하면 ChildObject의 myMethod에서 "Uncaught TypeError : undefined의 'call'속성을 읽을 수 없습니다"라는 오류가 발생했습니다.
zhekaus

@ zhekaus, 그것은 myMethod당신이 수업에 없는 것을 의미합니다 .
YemSalat

2
현재 this.myFun = function () {}을 사용하여 객체 메서드를 선언하므로 ParentClass.prototype.myFun.call (...) 호출이 작동하지 않으므로 CurrentClass.prototype.myFun.call ( ...). JS는 ... 쓰레기, 우리는 실제 OOP를 사용해야합니다.
Loenix

156

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을 사용하고 행복하십시오.


10
@ fsinisi90 나는 부모의 클래스 메소드에 관한 것이 아니라 ES6에서 슈퍼 키워드로 호출 할 수없는 부모의 인스턴스 메소드에 관한 질문이라고 생각합니다.
mcmlxxxiii

정적이 아닌 메소드에서도 작동합니다 (Chrome에서 테스트, 정적 키워드를 사용하지 않음)
Gianluca Casati

super전화 해야 합니까? "오래된"JS에 해당하는 것이 있습니까?
1252748

3
자식 클래스 생성자에서 super ()를 먼저 호출해야합니다.
user938363

1
@GianlucaCasati : super()정적 메소드 에서만 사용할 수 있습니다 . 생성자에서 사용한 것처럼 보입니다.
ZzZombo

5

이를 위해 ClassES6 의 추상화에 제한을 두지 않습니다 . 부모 생성자의 프로토 타입 메소드에 액세스하는 것은 __proto__속성을 통해 가능 합니다 (감가 상각된다는 동료 JS 코더가있을 것이라고 확신합니다). 특히 Array 하위 클래스 요구에 적합합니다). 따라서 __proto__내가 아는 모든 주요 JS 엔진에서 속성을 계속 사용할 수 있지만 ES6는 그 Object.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);
    }
}

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);
    }
}

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

2

다음은 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');


1

다단계 프로토 타입 조회를위한 훨씬 쉽고 컴팩트 한 솔루션이 있지만 Proxy지원 이 필요 합니다. 용도 : SUPER(<instance>).<method>(<args>)예컨대, 두 개의 클래스를 가정 용 AB extends A방법 m: SUPER(new B).m().

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

0

당신이 부모의 프로토 타입에 의해 부모의 메소드를 호출 할 수 있지만, 당신은 사용하기위한 현재의 아이의 인스턴스를 전달해야합니다 call, apply또는 bind방법을. 이 bind메소드는 새로운 함수를 생성하므로 한 번만 호출하는 것 외에는 성능에 신경 쓰지 않는 것이 좋습니다.

대안으로 원래 자식 메서드를 호출하는 동안 자식 메서드를 바꾸고 인스턴스에 부모 메서드를 넣을 수 있습니다.

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.