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.