プログラムによるJavaScript関数へのコードの追加


114

元のJSコードを変更せずに、既存のJSライブラリをカスタマイズしようとしています。このコードは、アクセスできるいくつかの外部JSファイルをロードします。私がやりたいことは、全体を2番目のJSファイルにコピーして貼り付けることなく、元のファイルに含まれている関数の1つを変更することです。
したがって、たとえば、立ち入り禁止のJSには、次のような機能があります。

var someFunction = function(){
    alert("done");
}

その関数になんらかのJSコードを追加または追加できるようにしたいのですが。その理由は主に、元の変更不可能なJSでは関数が非常に大きく、そのJSが更新されると、上書きした関数が古くなるためです。

これが可能かどうかは完全にはわかりませんが、確認したいと思いました。


3
最初の答えは、これはあなたを助ける必要があります
DarkAjax

コールバック関数のようなものが必要ですか?
sinemetu1

回答:


219

someFunctionがグローバルに利用可能な場合は、関数をキャッシュし、独自に作成して、それを呼び出すことができます。

これがオリジナルの場合...

someFunction = function() {
    alert("done");
}

あなたはこれをするでしょう...

someFunction = (function() {
    var cached_function = someFunction;

    return function() {
        // your code

        var result = cached_function.apply(this, arguments); // use .apply() to call it

        // more of your code

        return result;
    };
})();

ここにフィドルがあります


.applyキャッシュされた関数の呼び出しに使用していることに注意してください。これにより、の期待値を保持し、thisいくつあるかに関わらず、渡された引数を個別の引数として渡すことができます。


10
この答えは本当に一番上にあるはずです-それは問題の関数の機能を維持します...私からの+1。
リード

17
使用するための+1 apply:これは本当に問題を解決する唯一の答えです。
lonesomeday

2
@minitech:何... JavaScriptのfuncitonキーワードに慣れていませんか?;)編集ありがとう

1
@gdoron:コメントを追加しました。私が今本当に混乱しているのでなければ、実際のArgumentsオブジェクトをの2番目の引数として受け入れないブラウザーについては知りません.apply()。しかし、たとえばjQueryオブジェクトのような配列のようなオブジェクトである場合、一部のブラウザはエラーをスローします

2
この答えにより、インスタンス化されたオブジェクトを使用して、YouTube Iframe APIを介して埋め込まれた個々のYouTube動画を制御する問題が解決されます。各ビデオのデータを独自のモジュラー方式で処理するクラスを作成しようとしているときに、APIがコールバックが単一のグローバル関数である必要があるという事実を回避するために私がどれほど長い間努力してきたかはわかりません。あなたはその日のヒーローです。
Carnix 2017年

31

最初に実際の関数を変数に格納します。

var oldFunction = someFunction;

次に、独自のものを定義します。

someFunction = function(){
  // do something before
  oldFunction();
  // do something after
};

9
関数がメソッドの場合、applyそれを呼び出すために使用する必要があります。amの答えを参照してください。
hugomg、2012

それは私にとってはうまくいきsomeFunctionましたwindow.someFunctionが、上記のコードで置き換える必要がありました。その理由は、私の関数がjquery $(document).ready()ハンドラー内で宣言されたことです。
ネルソン

10

コードを呼び出す関数を作成してから、その関数を呼び出すことができます。

var old_someFunction = someFunction;
someFunction = function(){
    alert('Hello');
    old_someFunction();
    alert('Goodbye');
}

6

関数を更新できるかどうかはわかりませんが、関数の参照方法によっては、代わりに新しい関数を作成できます。

var the_old_function = someFunction;
someFunction = function () {
    /* ..My new code... */
    the_old_function();
    /* ..More of my new code.. */
}

5

また。ローカルコンテキストを変更する場合は、関数を再作成する必要があります。例えば:

var t = function() {
    var a = 1;
};

var z = function() {
    console.log(a);
};

z() // => log: undefined

その後

var ts = t.toString(),
    zs = z.toString();

ts = ts.slice(ts.indexOf("{") + 1, ts.lastIndexOf("}"));
zs = zs.slice(zs.indexOf("{") + 1, zs.lastIndexOf("}"));

var z = new Function(ts + "\n" + zs);

そして

z() // => log: 1

しかし、これは最も単純な例にすぎません。引数、コメント、戻り値を処理するにはまだ多くの作業が必要です。さらに、まだ多くの落とし穴があります。
toString | スライス | indexOf | lastIndexOf | 新しい機能


1

プロキシパターン(user1106925で使用)は、関数内に配置できます。以下に書いたものは、グローバルスコープにない関数やプロトタイプでさえ機能します。次のように使用します。

extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

以下のスニペットでは、test.prototype.doIt()を拡張する関数を使用しているのがわかります。

// allows you to prepend or append code to an existing function
function extender (container, funcName, prepend, append) {

    (function() {

        let proxied = container[funcName];

        container[funcName] = function() {
            if (prepend) prepend.apply( this );
            let result = proxied.apply( this, arguments );
            if (append) append.apply( this );
            return result;
        };

    })();

}

// class we're going to want to test our extender on
class test {
    constructor() {
        this.x = 'instance val';
    }
    doIt (message) {
        console.log(`logged: ${message}`);
        return `returned: ${message}`;
    }
}

// extends test.prototype.doIt()
// (you could also just extend the instance below if desired)
extender(
    test.prototype, 
    'doIt', 
    function () { console.log(`prepended: ${this.x}`) },
    function () { console.log(`appended: ${this.x}`) }
);

// See if the prepended and appended code runs
let tval = new test().doIt('log this');
console.log(tval);

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.