Javascript: étendre une fonction

La principale raison pour laquelle je le veux est que je veux étendre ma fonction initialize.

Quelque Chose comme ceci:

// main.js

window.onload = init();
function init(){
     doSomething();
}

// extend.js

function extends init(){
    doSomethingHereToo();
}

Donc je veux étendre une fonction comme j'étend une classe en PHP.

Et je voudrais l'étendre à partir d'autres fichiers aussi, donc par exemple j'ai la fonction init d'origine dans main.js et la fonction étendue dans extended.js.

73
demandé sur Adam 2011-01-02 15:48:25

5 réponses

Avec une vue plus large de ce que vous essayez réellement de faire et du contexte dans lequel vous le faites, je suis sûr que nous pourrions vous donner une meilleure réponse que la réponselittérale à votre question.

Mais voici une réponse littérale:

Si vous affectez ces fonctions à une propriété quelque part, vous pouvez envelopper la fonction d'origine et placer votre remplacement sur la propriété à la place:

// Original code in main.js
var theProperty = init;

function init(){
     doSomething();
}

// Extending it by replacing and wrapping, in extended.js
theProperty = (function(old) {
    function extendsInit() {
        old();
        doSomething();
    }

    return extendsInit;
})(theProperty);

Si vos fonctions ne sont pas déjà sur un objet, vous voudrez probablement les mettre là pour faciliter ce qui précède. Par exemple:

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {};

    publicSymbols.init = init;
    function init() {
    }

    return publicSymbols;
})();

// In extended.js
(function() {
    var oldInit = MyLibrary.init;
    MyLibrary.init = extendedInit;
    function extendedInit() {
        oldInit.apply(MyLibrary); // Use #apply in case `init` uses `this`
        doSomething();
    }
})();

, Mais il y a ces meilleures façons de le faire. Comme par exemple, fournir un moyen d'enregistrer des fonctions init.

// In main.js
var MyLibrary = (function() {
    var publicSymbols = {},
        initfunctions = [];

    publicSymbols.init = init;
    function init() {
        var funcs = initFunctions;

        initFunctions = undefined;

        for (index = 0; index < funcs.length; ++index) {
            try { funcs[index](); } catch (e) { }
        }
    }

    publicSymbols.addInitFunction = addInitFunction;
    function addInitFunction(f) {
        if (initFunctions) {
            // Init hasn't run yet, rememeber it
            initFunctions.push(f);
        }
        else {
            // `init` has already run, call it almost immediately
            // but *asynchronously* (so the caller never sees the
            // call synchronously)
            setTimeout(f, 0);
        }
    }

    return publicSymbols;
})();

(une grande partie de ce qui précède pourrait être écrite un peu plus compacte, mais je voulais utiliser des noms clairs comme publicSymbols plutôt que mon littéral d'objet pubs ou anonyme habituel. Vous pouvez l'écrire beaucoup plus compacte si vous voulez avoir des fonctions anonymes, mais Je ne me soucie pas beaucoup d'anonymous fonctions .)

85
répondu T.J. Crowder 2011-01-02 13:36:48

Il y a plusieurs façons de procéder, cela dépend de votre but, si vous voulez juste exécuter la fonction et dans le même contexte, vous pouvez utiliser .apply():

function init(){
  doSomething();
}
function myFunc(){
  init.apply(this, arguments);
  doSomethingHereToo();
}

Si vous voulez le remplacer par un init Plus Récent, cela ressemblerait à ceci:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
56
répondu Nick Craver 2011-01-02 13:07:15

Les autres méthodes sont géniales mais elles ne conservent aucune fonction prototype attachée à init. Pour contourner cela, vous pouvez faire ce qui suit (inspiré par le post de Nick Craver).

(function () {
    var old_prototype = init.prototype;
    var old_init = init;
    init = function () {
        old_init.apply(this, arguments);
        // Do something extra
    };
    init.prototype = old_prototype;
}) ();
4
répondu Ally 2012-09-10 09:49:50

Une autre option pourrait être:

var initial = function() {
    console.log( 'initial function!' );
}

var iWantToExecuteThisOneToo = function () {
    console.log( 'the other function that i wanted to execute!' );
}

function extendFunction( oldOne, newOne ) {
    return (function() {
        oldOne();
        newOne();
    })();
}

var extendedFunction = extendFunction( initial, iWantToExecuteThisOneToo );
3
répondu Sérgio 2015-02-06 15:20:00

Utilisez extendFunction.js

init = extendFunction(init, function(args) {
  doSomethingHereToo();
});

Mais dans votre cas spécifique, il est plus facile d'étendre la fonction onload globale:

extendFunction('onload', function(args) {
  doSomethingHereToo();
});

En fait, j'aime vraiment votre question, cela me fait penser à différents cas d'utilisation.

Pour les événements javascript, vous voulez vraiment ajouter et supprimer des gestionnaires - mais pour extendFunction, comment pourriez-vous faire plus tard supprime, fonctionnalité? Je pourrais facilement ajouter un .retourne la méthode aux fonctions étendues, donc init = init.revert() renverrait l'original fonction. Évidemment, cela pourrait conduire à un code assez mauvais, mais peut-être que cela vous permet de faire quelque chose sans toucher une partie étrangère de la base de code.

-2
répondu Devin G Rhode 2013-04-04 05:34:58