JavaScript 프로토 타입을 사용하는 호출 방법


129

재정의 된 경우 JavaScript의 프로토 타입 메소드에서 기본 메소드를 호출 할 수 있습니까?

MyClass = function(name){
    this.name = name;
    this.do = function() {
        //do somthing 
    }
};

MyClass.prototype.do = function() {  
    if (this.name === 'something') {
        //do something new
    } else {
        //CALL BASE METHOD
    }
};

기본 방법은 무엇입니까? 생성자에서 this.do = function () {}?
— Ionuț G. Stan

답변:


200

정확히 무엇을하려고하는지 이해하지 못했지만 일반적으로 객체 별 동작을 구현하는 것은 다음 라인을 따라 수행됩니다.

function MyClass(name) {
    this.name = name;
}

MyClass.prototype.doStuff = function() {
    // generic behaviour
}

var myObj = new MyClass('foo');

var myObjSpecial = new MyClass('bar');
myObjSpecial.doStuff = function() {
    // do specialised stuff
    // how to call the generic implementation:
    MyClass.prototype.doStuff.call(this /*, args...*/);
}

1
나는 이것을 얻지 못했다. 나는 markvpc와 동일한 목적으로 정확하게 여기에왔다. 내 말은, 프로토 타입의 다른 방법에서 사용되는 일종의 "비공개"방법을 원합니다. 귀하의 답변에 따라 공장을 만들어야하며 피할 수있는 방법이 있는지 알고 싶습니다.
— R01010010

10
JS에서 "클래스"단어는 혼동을 일으키므로 사용하지 마십시오. JavaScript에는 클래스가 없습니다
— Polaris

1
인스턴스화 된 객체에만 작동합니다. 모든 인스턴스를 무시하려면 어떻게합니까?
— supertonsky

나를 위해 작동합니다! 정확히 내가 필요한 것. 부모로부터 상속받은 4 개의 생성자가 있습니다. 그 중 두 개는 프로토 타입 인 기본 생성자에서 메소드를 대체하고 호출해야합니다. 이것은 내가 정확히 그것을 할 수있게했습니다.
— binarygiant

3
나는 이것이 왜 그렇게 많은 투표를했는지 이해하지 못합니다. 이는 서브 클래스의 모든 인스턴스가 아니라 기본 클래스의 한 인스턴스에 대한 동작 만 대체합니다. 따라서 이것은 질문에 대답하지 않습니다.
— BlueRaja-대니 Pflughoeft

24

이를 수행하는 한 가지 방법은 기본 메소드를 저장 한 다음 대체 메소드에서이를 호출하는 것입니다.

MyClass.prototype._do_base = MyClass.prototype.do;
MyClass.prototype.do = function(){  

    if (this.name === 'something'){

        //do something new

    }else{
        return this._do_base();
    }

};

11
이것은 무한 재귀를 만듭니다.
— Johan Tidén

3
나는 거기에 있었다 : 무한 재귀.
— jperelli

else 절은 prototype.do에 대한 참조 인 _do_base를 호출하고 실행 중입니다. 매개 변수 (또는 상태)가 변경되지 않았으므로 코드는 다시 _do_base를 호출하여 else로 이동합니다.
— Johan Tidén

10
@ JohanTidén은 _do_base이전에 정의 된 기능에 대한 참조를 저장합니다. 없는 경우이었습니다 undefined. 자바 스크립트는 아닙니다 make– 표현은 당신이 제안한 것처럼 게으르게 평가 되지 않습니다 . 상속 된 프로토 타입 도 _do_base를 사용하여 기본 유형의 구현이라고 생각하는 것을 저장 do하면 문제가있는 것입니다. 따라서 클로저는이 메소드를 사용하는 경우 원래 함수 구현에 대한 참조를 저장하는 더 나은 상속 안전 방법 Function.prototype.call(this)입니다.
— binki

16

나는 당신의 모범이 당신의 생각대로 작동하지 않을까 두려워합니다. 이 부분:

this.do = function(){ /*do something*/ };

의 정의를 덮어 씁니다.

MyClass.prototype.do = function(){ /*do something else*/ };

새로 작성된 오브젝트에는 이미 "do"특성이 있으므로 프로토 타입 체인을 찾지 않습니다.

Javascript의 고전적인 상속 형태는 어색하며 파악하기가 어렵습니다. Douglas Crockfords 간단한 상속 패턴을 대신 사용하는 것이 좋습니다. 이처럼 :

function my_class(name) {
    return {
        name: name,
        do: function () { /* do something */ }
    };
}

function my_child(name) {
    var me = my_class(name);
    var base_do = me.do;
    me.do = function () {
        if (this.name === 'something'){
            //do something new
        } else {
            base_do.call(me);
        }
    }
    return me;
}

var o = my_child("something");
o.do(); // does something new

var u = my_child("something else");
u.do(); // uses base function

제 생각에는 자바 스크립트에서 객체, 생성자 및 상속을 처리하는 훨씬 명확한 방법입니다. Crockfords Javascript : The good parts 에서 더 많은 내용을 읽을 수 있습니다 .


3
실제로 매우 훌륭하지만 실제로 는 원래 메서드 에서 바인딩 base_do을 잃어 버렸기 때문에 실제로 함수로 호출 할 수 없습니다 . 따라서 기본 메소드의 설정은 좀 더 복잡합니다. 특히 하위 오브젝트 대신 기본 오브젝트를 사용하여 호출하려는 경우에 특히 그렇습니다 . 나는 비슷한 것을 제안 할 것이다 . thisdothisbase_do.apply(me, arguments)
— Giulio Piancastelli

그냥 궁금, 왜 사용하지 않는 var위해 base_do? 이것이 의도적 인 것이며, 그렇다면 그 이유는 무엇입니까? 그리고 @GiulioPiancastelli가 말했듯이,이 this바인딩 이 바인딩 내에서 끊어 base_do()지거나 그 호출 this이 호출자의 상속을 상속 합니까?
— binki

@ binki 예제를 업데이트했습니다. 는 var이 있어야합니다. 사용 call(또는 apply) 우리가 바인딩 할 수 this올바르게.
— Magnar

10

나는이 게시물이 4 년 전이라는 것을 알고 있지만 C # 배경으로 인해 클래스 이름을 지정하지 않고 기본 클래스를 호출하는 방법을 찾고 있었지만 하위 클래스의 속성으로 가져옵니다. 그래서 Christoph의 대답에 대한 나의 유일한 변화 는

이것으로부터:

MyClass.prototype.doStuff.call(this /*, args...*/);

이에:

this.constructor.prototype.doStuff.call(this /*, args...*/);

6
이 작업을 수행하지 마십시오 . this.constructor항상을 가리킬 수는 없습니다 MyClass.
— Bergi

누군가가 상속을 설정하지 않았다면 글쎄요. 'this'는 항상 최상위 개체를 참조해야하며 'constructor'속성은 항상 자체 생성자 함수를 가리켜 야합니다.
— Triynko

5

이와 같은 함수를 정의하면 (OPP 사용)

function Person(){};
Person.prototype.say = function(message){
   console.log(message);
}

프로토 타입 함수를 호출하는 두 가지 방법이 있습니다. 1) 인스턴스를 만들고 객체 함수를 호출합니다.

var person = new Person();
person.say('hello!');

그리고 다른 방법은 ... 2) 프로토 타입에서 직접 함수를 호출하는 것입니다.

Person.prototype.say('hello there!');

5

이 솔루션은 Object.getPrototypeOf

TestA 가지고있는 슈퍼 getName

TestB재정의하는 아이입니다 getName만, 또한이 getBothNames그 통화량 super의 버전 getName뿐만 아니라 child버전

function TestA() {
  this.count = 1;
}
TestA.prototype.constructor = TestA;
TestA.prototype.getName = function ta_gn() {
  this.count = 2;
  return ' TestA.prototype.getName is called  **';
};

function TestB() {
  this.idx = 30;
  this.count = 10;
}
TestB.prototype = new TestA();
TestB.prototype.constructor = TestB;
TestB.prototype.getName = function tb_gn() {
  return ' TestB.prototype.getName is called ** ';
};

TestB.prototype.getBothNames = function tb_gbn() {
  return Object.getPrototypeOf(TestB.prototype).getName.call(this) + this.getName() + ' this object is : ' + JSON.stringify(this);
};

var tb = new TestB();
console.log(tb.getBothNames());


4
function NewClass() {
    var self = this;
    BaseClass.call(self);          // Set base class

    var baseModify = self.modify;  // Get base function
    self.modify = function () {
        // Override code here
        baseModify();
    };
}

4

대안 :

// shape 
var shape = function(type){
    this.type = type;
}   
shape.prototype.display = function(){
    console.log(this.type);
}
// circle
var circle = new shape('circle');
// override
circle.display = function(a,b){ 
    // call implementation of the super class
    this.__proto__.display.apply(this,arguments);
}

작동 할 수도 있지만이 솔루션을 권장하지 않습니다. __proto__(오브젝트의 [[Prototype]]`을 변경하는) 사용을 피해야하는 많은 이유 가 있습니다. Object.create(...)대신 경로를 사용하십시오 .
— KarelG

2

올바르게 이해하면 기본 기능을 항상 수행하고 그 중 일부는 구현에 맡겨야합니다.

' template method '디자인 패턴으로 도움을받을 수 있습니다 .

Base = function() {}
Base.prototype.do = function() { 
    // .. prologue code
    this.impldo(); 
    // epilogue code 
}
// note: no impldo implementation for Base!

derived = new Base();
derived.impldo = function() { /* do derived things here safely */ }

1

슈퍼 클래스를 이름으로 알고 있다면 다음과 같이 할 수 있습니다.

function Base() {
}

Base.prototype.foo = function() {
  console.log('called foo in Base');
}

function Sub() {
}

Sub.prototype = new Base();

Sub.prototype.foo = function() {
  console.log('called foo in Sub');
  Base.prototype.foo.call(this);
}

var base = new Base();
base.foo();

var sub = new Sub();
sub.foo();

이것은 인쇄됩니다

called foo in Base
called foo in Sub
called foo in Base

예상대로.


1

ES5의 또 다른 방법은 다음을 사용하여 프로토 타입 체인을 명시 적으로 순회하는 것입니다. Object.getPrototypeOf(this)

const speaker = {
  speak: () => console.log('the speaker has spoken')
}

const announcingSpeaker = Object.create(speaker, {
  speak: {
    value: function() {
      console.log('Attention please!')
      Object.getPrototypeOf(this).speak()
    }
  }
})

announcingSpeaker.speak()

0

아니요, 생성자에는 do 함수를, 프로토 타입에는 do 함수에 다른 이름을 지정해야합니다.


0

또한 하나의 특수 인스턴스뿐만 아니라 모든 인스턴스 를 재정의 하려는 경우이 인스턴스가 도움이 될 수 있습니다.

function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

myObj = new MyClass();
myObj.myMethod();

결과:

doing original
doing override

-1
function MyClass() {}

MyClass.prototype.myMethod = function() {
  alert( "doing original");
};
MyClass.prototype.myMethod_original = MyClass.prototype.myMethod;
MyClass.prototype.myMethod = function() {
  MyClass.prototype.myMethod_original.call( this );
  alert( "doing override");
};

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