web-dev-qa-db-fra.com

Redéfinition d'une fonction JavaScript lors du référencement de l'original

J'ai une fonction, a(), que je veux remplacer, mais je souhaite également que l'original a() soit exécuté dans un ordre dépendant du contexte. Par exemple, parfois, lorsque je génère une page, je souhaite remplacer comme suit:

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

et parfois comme ça:

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

Comment puis-je obtenir cette original_a() de l'intérieur de la a() dominante? Est-ce même possible?

S'il vous plaît, ne suggérez pas d'alternative à la conduite excessive de cette façon, j'en connais beaucoup. Je demande à propos de cette façon en particulier.

155
Kev

Vous pouvez faire quelque chose comme ça:

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

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

Déclarer original_a dans une fonction anonyme l'empêche d'encombrer l'espace de noms global, mais il est disponible dans les fonctions internes.

Comme Nerdmaster mentionné dans les commentaires, veillez à inclure le () à la fin. Vous souhaitez appeler la fonction externe et stocker le résultat (l'une des deux fonctions internes) dans a, mais pas la fonction externe elle-même dans a.

177
Matthew Crumley

Le modèle de proxy pourrait vous aider:

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

Ce qui précède englobe son code dans une fonction pour masquer la variable "soumise au proxy". Il enregistre la méthode setArray de jQuery dans une fermeture et la remplace. Le proxy enregistre ensuite tous les appels vers la méthode et délègue l'appel à l'original. Utiliser apply (this, arguments) garantit que l'appelant ne sera pas en mesure de remarquer la différence entre la méthode d'origine et la méthode proxy.

82
PhilHoy

Merci les gars le modèle de proxy a vraiment aidé ..... En fait, je voulais appeler une fonction globale foo .. Dans certaines pages, j'ai besoin de faire des vérifications. Alors j'ai fait ce qui suit.

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

Thnx cela m'a vraiment aidé

27
Naresh S

Vous pouvez remplacer une fonction en utilisant une construction comme celle-ci:

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

Par exemple:

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

Edit: Correction d'une faute de frappe.

9
Hai Phan

Passer des arguments arbitraires:

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

La réponse fournie par @Matthew Crumley utilise les expressions de fonction immédiatement invoquées pour fermer la fonction "a" la plus ancienne dans le contexte d'exécution de la fonction renvoyée. Je pense que c'était la meilleure réponse, mais personnellement, je préférerais passer la fonction 'a' comme argument à l'IIFE. Je pense que c'est plus compréhensible.

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

Les exemples ci-dessus n'appliquent pas correctement this ou ne transmettent pas arguments correctement à la substitution de fonction. Le trait de soulignement _.wrap () encapsule les fonctions existantes, applique this et transmet correctement arguments. Voir: http://underscorejs.org/#wrap

1
nevf

J'ai créé un petit assistant pour un scénario similaire car j'avais souvent besoin de remplacer des fonctions de plusieurs bibliothèques. Cet assistant accepte un "espace de noms" (le conteneur de fonctions), le nom de la fonction et la fonction de substitution. Il remplacera la fonction d'origine dans l'espace de nom référencé par la nouvelle.

La nouvelle fonction accepte la fonction d'origine en tant que premier argument et les arguments de la fonction d'origine en tant que reste. Cela préservera le contexte à chaque fois. Il prend également en charge les fonctions void et 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)));
    };
}

Utilisation par exemple avec Bootstrap:

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

Je n'ai cependant pas créé de test de performance. Cela peut éventuellement ajouter des frais généraux indésirables qui peuvent ou ne peuvent pas être un gros problème, selon les scénarios.

0
Zoltán Tamási

J'avais du code écrit par quelqu'un d'autre et je voulais ajouter une ligne à une fonction que je ne pouvais pas trouver dans le code. Donc, comme solution de contournement, je voulais le remplacer.

Aucune des solutions n'a fonctionné pour moi cependant.

Voici ce qui a fonctionné dans mon cas:

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

À mon avis, les principales réponses ne sont pas lisibles/gérables et les autres réponses ne lient pas correctement le contexte. Voici une solution lisible utilisant la syntaxe ES6 pour résoudre ces deux problèmes.

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

Ma réponse a donc été une solution qui me permet d’utiliser la variable _this pointant vers l’objet d’origine. Je crée une nouvelle instance d'un "Square", mais je détestais la façon dont le "Square" générait sa taille. Je pensais que cela devrait suivre mes besoins spécifiques. Cependant, pour ce faire, il me fallait une case "GetSize" mise à jour avec les éléments internes de cette fonction appelant d'autres fonctions déjà existantes dans la case, comme this.height, this.GetVolume (). Mais pour ce faire, je devais le faire sans piratage fou. Alors voici ma solution.

Une autre fonction d'initialisation ou d'assistance d'objet.

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

Fonction dans l'autre objet.

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