Adicionar código a uma função javascript programaticamente


114

Estou tentando personalizar uma biblioteca JS existente sem modificar o código JS original. Este código é carregado em alguns arquivos JS externos aos quais tenho acesso e o que gostaria de fazer é alterar uma das funções contidas no arquivo original sem copiar e colar tudo no segundo arquivo JS.
Então, por exemplo, o fora dos limites JS pode ter uma função como esta:

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

Eu gostaria de poder acrescentar ou acrescentar algum código JS a essa função. A razão é principalmente que no JS original intocável a função é muito grande e se esse JS for atualizado, a função que eu sobrescrever ficará desatualizada.

Não tenho certeza se isso é possível, mas decidi verificar.


3
a primeira resposta de este deve ajudá-lo
DarkAjax

Você está querendo algo como uma função de retorno de chamada?
sinemetu1

Respostas:


220

Se someFunctionestiver disponível globalmente, você pode armazenar a função em cache, criar a sua própria e fazer com que a sua a chame.

Então, se este é o original ...

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

Você faria isso ...

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

Aqui está o violino


Observe que eu uso .applypara chamar a função em cache. Isso me permite reter o valor esperado de thise passar quaisquer argumentos que foram passados ​​como argumentos individuais, independentemente de quantos eram.


10
Esta resposta deve realmente ser a principal - ela preserva a funcionalidade da função em questão ... +1 de mim.
Reid

17
+1 para usar apply: esta é a única resposta que realmente resolve o problema.
solitário dia

2
@minitech: O quê ... Você não está familiarizado com a funcitonpalavra-chave do JavaScript ? ;) Obrigado pela edição

1
@gdoron: Acabei de adicionar um comentário. A menos que eu esteja realmente confuso agora, não conheço nenhum navegador que não aceite um objeto Arguments real como o segundo argumento para .apply(). Mas se fosse um objeto do tipo Array, como um objeto jQuery, por exemplo, então sim, alguns navegadores

2
Essa resposta, na verdade, resolve o problema de usar objetos instanciados para controlar vídeos individuais do YouTube incorporados por meio da API Iframe do YouTube. Você não tem ideia de quanto tempo estou tentando contornar o fato de que a API exige que seu retorno de chamada seja uma única função global quando estou tentando criar uma classe para lidar com os dados de cada vídeo de uma maneira modular única. Você é meu herói do dia.
Carnix

31

primeiro armazene a função real em uma variável.

var oldFunction = someFunction;

em seguida, defina o seu próprio:

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

9
Se sua função for um método, você precisará usar applypara chamá-la. Veja a resposta de am.
hugomg

Ela trabalhou para mim, mas necessário para substituir someFunctioncom window.someFunctionno código acima. A razão é que minha função foi declarada dentro de um $(document).ready()manipulador jquery .
Nelson

10

Você pode fazer uma função que chama seu código e, em seguida, chama a função.

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

6

Não sei se você pode atualizar a função, mas dependendo de como ela é referenciada, você pode fazer uma nova função em seu lugar:

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

5

Além disso. Se você quiser mudar o contexto local, terá que recriar a função. Por exemplo:

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

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

Agora

z() // => log: undefined

Então

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

Mas este é apenas o exemplo mais simples. Ainda vai dar muito trabalho para lidar com argumentos, comentários e valor de retorno. Além disso, ainda existem muitas armadilhas.
toString | fatia | indexOf | lastIndexOf | nova função


1

O padrão de proxy (conforme usado pelo user1106925) pode ser colocado dentro de uma função. O que escrevi abaixo funciona em funções que não estão no escopo global e até mesmo em protótipos. Você o usaria assim:

extender(
  objectContainingFunction,
  nameOfFunctionToExtend,
  parameterlessFunctionOfCodeToPrepend,
  parameterlessFunctionOfCodeToAppend
)

No snippet abaixo, você pode me ver usando a função para estender 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);

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.