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 2014

10
JSでは混乱を招くため、「クラス」という言葉は使用しないでください。JavaScriptにはクラスがありません
Polaris

1
これは、インスタンス化されたオブジェクトに対してのみ機能します。すべてのインスタンスをオーバーライドしたい場合はどうなりますか?
supertonsky 2015年

私のために働く!まさに私が必要としたもの。親から継承する4つのコンストラクタがあります。それらの2つは、プロトタイプである基本コンストラクターのメソッドをオーバーライドして呼び出す必要があります。これにより、まさにそれが可能になりました。
バイナリジャイアント、2015

3
なぜこれが非常に多くの賛成票を持ち、正しいとマークされているのか理解できません。これは、サブクラスのすべてのインスタンスではなく、基本クラスの1つのインスタンスの動作のみをオーバーライドします。したがって、これは質問に答えません。
BlueRaja-ダニープフルフフト2015

24

それを行う1つの方法は、基本メソッドを保存してから、オーバーライドされたメソッドからそれを呼び出すことです。

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

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

        //do something new

    }else{
        return this._do_base();
    }

};

11
これは無限再帰を作成します。
JohanTidén2012

3
私は行ったことがある:無限再帰。
jperelli 2013年

else句は、prototype.doへの参照である_do_baseを呼び出して実行しています。変更されたパラメーター(または状態)がないため、コードは再びelseに入り、_do_baseを再度呼び出します。
JohanTidén、2015年

10
@JohanTidénは、_do_base以前に定義された関数への参照を格納します。何もなかった場合、それはになりますundefined。JavaScriptはそうではありませんmake—式は、あなたが提案するように遅延評価されることはありません。さて、プロトタイプはから継承された場合も、それは、その基本型の実装であると考えるものを格納するために使用_do_base do、その後、あなたは問題を持っています。したがって、そうです、このメソッドを使用する場合、クロージャーは元の関数実装への参照を格納するための継承セーフな方法です(ただし、を使用する必要がありFunction.prototype.call(this)ます)。
binki

16

申し訳ありませんが、あなたの例はあなたの考えているようには機能しません。この部分:

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

の定義を上書きします

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

新しく作成されたオブジェクトにはすでに「do」プロパティがあるため、プロトタイプチェーンを検索しません。

Javascriptの継承の古典的な形式は奇妙で、把握するのが困難です。代わりに、ダグラス・クロックフォードの単純な継承パターンを使用することをお勧めします。このような:

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

私の意見では、JavaScriptでオブジェクト、コンストラクター、継承を処理するためのより明確な方法です。詳細については、Crockfords Javascript:The good partsを参照してください


3
確かに非常に素晴らしいですが、元のメソッドのバインディングがそのようにbase_do失われるため、実際には関数として呼び出すことはできません。したがって、特に子オブジェクトの代わりに基本オブジェクトを使用して呼び出す場合は、基本メソッドの設定が少し複雑になります。に似たものをお勧めします。thisdothisbase_do.apply(me, arguments)
Giulio Piancastelli 2014年

ただ、不思議、なぜあなたが使用していないvarためbase_do?これは意図的ですか、もしそうなら、なぜですか?そして、@ GiulioPiancastelliが言ったように、これはthisバインディングを壊しますbase_do()か、それともthisその呼び出しは呼び出し元からを継承しますか?
binki

@binki例を更新しました。あるvarはずです。使用するとcall(またはapply)、私たちが結合することを可能にするthis適切に。
Magnar

10

私はこの投稿が4年前のものであることを知っていますが、C#の背景により、クラス名を指定せずにサブクラスのプロパティによって取得することなく、基本クラスを呼び出す方法を探していました。クリストフの答えに対する私の唯一の変更は

これから:

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

これに:

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

6
これを行わないでください。this.constructor常にを指すとは限りませんMyClass
Bergi

まあ、誰かが継承の設定を台無しにしない限り、そうすべきです。'this'は常に最上位のオブジェクトを参照し、その 'constructor'プロパティは常に独自のコンストラクター関数を指す必要があります。
Triynko 2017

5

このような関数を定義した場合(OOPを使用)

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

プロトタイプ関数を呼び出す方法は2つあります。1)インスタンスを作成してオブジェクト関数を呼び出します。

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

もう1つは... 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、2016

2

私が正しく理解していれば、Baseの機能を常に実行する必要がありますが、その一部は実装に任せる必要があります。

テンプレートメソッド」のデザインパターンが役立つ場合があります。

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のもう1つの方法は、明示的にプロトタイプチェーンをトラバースすることです。 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

さらに、その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();

結果:

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.