Aggiunta di codice a una funzione javascript a livello di codice


114

Sto tentando di personalizzare una libreria JS esistente senza modificare il codice JS originale. Questo codice viene caricato in alcuni file JS esterni a cui ho accesso e quello che vorrei fare è modificare una delle funzioni contenute nel file originale senza copiare e incollare l'intera cosa nel secondo file JS.
Quindi, ad esempio, il JS off limits potrebbe avere una funzione come questa:

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

Mi piacerebbe essere in grado in qualche modo di aggiungere o anteporre del codice JS in quella funzione. Il motivo è principalmente che nel JS intoccabile originale la funzione è piuttosto enorme e se quel JS viene aggiornato, la funzione con cui lo sovrascrivo sarà obsoleta.

Non sono del tutto sicuro che sia possibile, ma ho pensato di controllare.


3
la prima risposta di questo dovrebbe aiutare
DarkAjax

Vuoi qualcosa come una funzione di callback?
sinemetu1

Risposte:


220

Se someFunctionè disponibile a livello globale, puoi memorizzare la funzione nella cache, crearne una tua e farla chiamare dalla tua.

Quindi se questo è l'originale ...

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

Faresti questo ...

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;
    };
})();

Ecco il violino


Si noti che utilizzo .applyper chiamare la funzione memorizzata nella cache. Questo mi consente di mantenere il valore atteso di thise di passare tutti gli argomenti passati come argomenti individuali indipendentemente da quanti erano.


10
Questa risposta dovrebbe essere davvero la prima: preserva la funzionalità della funzione in questione ... +1 da parte mia.
Reid

17
+1 per l'utilizzo apply: questa è l'unica risposta che risolve davvero il problema.
solomeday

2
@minitech: Cosa ... Non hai familiarità con la funcitonparola chiave JavaScript ? ;) Grazie per la modifica

1
@gdoron: ho appena aggiunto un commento. A meno che non sia davvero confuso in questo momento, non conosco nessun browser che non accetterà un oggetto Arguments effettivo come secondo argomento per .apply(). Ma se fosse un oggetto tipo Array come un oggetto jQuery, ad esempio, allora sì, alcuni browser genererebbero un errore

2
Questa risposta, come accade, risolve il problema dell'utilizzo di oggetti istanziati per controllare i singoli video di YouTube incorporati tramite l'API Iframe di YouTube. Non hai idea da quanto tempo cerco di aggirare il fatto che l'API richiede che il suo callback sia una singola funzione globale quando cerco di creare una classe per gestire i dati di ogni video in un modo modulare unico. Sei il mio eroe per la giornata.
Carnix

31

prima memorizzare la funzione effettiva in una variabile ..

var oldFunction = someFunction;

quindi definisci il tuo:

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

9
Se la tua funzione è un metodo che dovrai usare applyper chiamarla. Vedi la risposta di am.
hugomg

Ha funzionato per me, ma doveva essere sostituito someFunctioncon window.someFunctionil codice sopra. Il motivo è che la mia funzione è stata dichiarata all'interno di un $(document).ready()gestore jquery .
Nelson,

10

Puoi creare una funzione che chiama il tuo codice e quindi chiama la funzione.

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

6

Non so se puoi aggiornare la funzione, ma a seconda di come viene referenziato, puoi creare una nuova funzione al suo posto:

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

5

Anche. Se vuoi cambiare il contesto locale devi ricreare function. Per esempio:

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

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

Adesso

z() // => log: undefined

Poi

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);

E

z() // => log: 1

Ma questo è solo l'esempio più semplice. Ci vorrà ancora molto lavoro per gestire argomenti, commenti e valore di ritorno. Inoltre, ci sono ancora molte insidie.
toString | fetta | indexOf | lastIndexOf | nuova funzione


1

Il modello proxy (utilizzato da user1106925) può essere inserito in una funzione. Quello che ho scritto di seguito funziona su funzioni che non rientrano nell'ambito globale e persino sui prototipi. Lo useresti in questo modo:

extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

Nello snippet qui sotto, puoi vedermi usare la funzione per estendere 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);

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.