web-dev-qa-db-ja.com

オリジナルを参照しながらJavaScript関数をオーバーライドする

a()というオーバーライドする関数がありますが、元のa()もコンテキストに応じた順序で実行されます。たとえば、ページを生成しているときに、次のようにオーバーライドしたい場合があります。

function a() {
    new_code();
    original_a();
}

そして時々このように:

function a() {
    original_a();
    other_new_code();
}

オーバーライドするoriginal_a()内からa()を取得するにはどうすればよいですか?それも可能ですか?

この方法でオーバーライドする代替案を提案しないでください、私は多くを知っています。私はこの方法について具体的に尋ねています。

155
Kev

次のようなことができます:

var a = (function() {
    var original_a = a;

    if (condition) {
        return function() {
            new_code();
            original_a();
        }
    } else {
        return function() {
            original_a();
            other_new_code();
        }
    }
})();

匿名関数内でoriginal_aを宣言すると、グローバル名前空間が乱雑になることはありませんが、内部関数で使用できます。

コメントで言及されたNerdmasterのように、最後に必ず()を含めてください。外部関数自体をaに格納するのではなく、外部関数を呼び出してresult(2つの内部関数の1つ)をaに格納します。

177
Matthew Crumley

プロキシパターン は次の場合に役立ちます。

(function() {
    // log all calls to setArray
    var proxied = jQuery.fn.setArray;
    jQuery.fn.setArray = function() {
        console.log( this, arguments );
        return proxied.apply( this, arguments );
    };
})();

上記の例では、コードを関数でラップして、「プロキシ」変数を非表示にします。 jQueryのsetArray-methodをクロージャーに保存して上書きします。プロキシは、メソッドへのすべての呼び出しを記録し、元の呼び出しを委任します。 apply(this、arguments)を使用すると、呼び出し元が元のメソッドとプロキシされたメソッドの違いに気付かないことが保証されます。

82
PhilHoy

プロキシパターンが本当に助けてくれてありがとう。だから私は次のことをした。

//Saving the original func
var org_foo = window.foo;

//Assigning proxy fucnc
window.foo = function(args){
    //Performing checks
    if(checkCondition(args)){
        //Calling original funcs
        org_foo(args);
    }
};

これは本当に助かりました

27
Naresh S

次のような構造を使用して関数をオーバーライドできます。

function override(f, g) {
    return function() {
        return g(f);
    };
}

例えば:

 a = override(a, function(original_a) {
      if (condition) { new_code(); original_a(); }
      else { original_a(); other_new_code(); }
 });

編集:タイプミスを修正。

9
Hai Phan

任意の引数を渡す:

a = override(a, function(original_a) {
    if (condition) { new_code(); original_a.apply(this, arguments) ; }
    else { original_a.apply(this, arguments); other_new_code(); }
});
4
Edric Garran

@Matthew Crumleyが提供する答えは、すぐに呼び出された関数式を使用して、返された関数の実行コンテキストに古い「a」関数を閉じることです。これが最良の答えだったと思いますが、個人的には、関数 'a'をIIFEへの引数として渡すことを好みます。もっと理解しやすいと思います。

   var a = (function(original_a) {
        if (condition) {
            return function() {
                new_code();
                original_a();
            }
        } else {
            return function() {
                original_a();
                other_new_code();
            }
        }
    })(a);
1

上記の例は、thisを正しく適用したり、argumentsを関数オーバーライドに正しく渡したりしません。アンダースコア_.wrap()は、既存の関数をラップし、thisを適用し、argumentsを正しく渡します。参照: http://underscorejs.org/#wrap

1
nevf

多くの場合、いくつかのライブラリの関数をオーバーライドする必要があるため、同様のシナリオ用の小さなヘルパーを作成しました。このヘルパーは、「名前空間」(関数コンテナー)、関数名、およびオーバーライド関数を受け入れます。参照されたネームスペースの元の関数を新しいものに置き換えます。

新しい関数は、元の関数を最初の引数として受け入れ、元の関数の引数を残りとして受け入れます。常にコンテキストを保持します。 voidおよびnon-void関数もサポートします。

function overrideFunction(namespace, baseFuncName, func) {
    var originalFn = namespace[baseFuncName];
    namespace[baseFuncName] = function () {
        return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
    };
}

たとえば、Bootstrapの使用法:

overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
    // ... do stuff before base call
    baseFn(obj);
    // ... do stuff after base call
});

ただし、パフォーマンステストは作成しませんでした。シナリオに応じて、大したこともできないこともある、望ましくないオーバーヘッドが追加される可能性があります。

0
Zoltán Tamási

私は他の誰かが書いたコードを持っていて、コード内で見つけることができなかった関数に行を追加したかった。回避策として、それをオーバーライドしたかったのです。

しかし、どのソリューションも私にとってはうまくいきませんでした。

これが私の場合にうまくいったものです:

if (typeof originalFunction === "undefined") {
    originalFunction = targetFunction;
    targetFunction = function(x, y) {
        //Your code
        originalFunction(a, b);
        //Your Code
    };  
}
0
Sagar Sodah

私の意見では、上位の回答は読み取り/保守ができず、他の回答はコンテキストを適切にバインドしません。 ES6構文を使用してこれらの両方の問題を解決する読みやすいソリューションを次に示します。

const orginial = someObject.foo;
someObject.foo = function() {
  if (condition) orginial.bind(this)(...arguments);
};
0
James L.

したがって、私の答えは、元のオブジェクトを指す_this変数を使用できるソリューションになりました。 「Square」の新しいインスタンスを作成しますが、「Square」がサイズを生成する方法が嫌いです。私はそれが私の特定のニーズに従うべきだと思った。ただし、そうするためには、正方形にthis.height、this.GetVolume()などの正方形に既に存在する他の関数を呼び出す関数の内部を持つ更新された「GetSize」関数が必要でした。しかし、そうするためには、クレイジーなハックなしでこれを行う必要がありました。だからここに私の解決策があります。

他のオブジェクト初期化子またはヘルパー関数。

this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
  this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons =  this.updateToolbarButtons(viewer);

他のオブジェクトの関数。

updateToolbarButtons = function(viewer) {
  var _viewer = viewer;
  return function(width, height){ 
blah blah black sheep I can refer to this.anything();
}
};
0
SteckDEV