Javascript: Extend a Function

Głównym powodem, dla którego tego chcę, jest to, że chcę rozszerzyć moją funkcję initialize.

Coś takiego:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

Więc chcę rozszerzyć funkcję tak jak rozszerzam klasę w PHP.

I chciałbym rozszerzyć ją również z innych plików, więc na przykład mam oryginalną funkcję init w main.js i rozszerzoną funkcję w extended.js.

Author: Adam, 2011-01-02

5 answers

Z szerszym spojrzeniem na to, co naprawdę próbujesz zrobić i kontekst, w którym to robisz, jestem pewien, że możemy dać ci lepszą odpowiedź niż literalna odpowiedź na twoje pytanie.

Ale oto dosłowna odpowiedź:

Jeśli przypisujesz te funkcje do jakiejś właściwości, możesz zawinąć oryginalną funkcję i zamiast tego umieścić zamiennik na właściwości:

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

Jeśli twoje funkcje nie są już na obiekcie, prawdopodobnie chcesz je umieścić tam, aby ułatwić powyższe. Na przykład:

// 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();
    }
})();
Ale są na to lepsze sposoby. Jak na przykład zapewnienie sposobu rejestracji init funkcji.
// 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;
})();

(Wiele z powyższych można by napisać nieco bardziej zwięźle, ale chciałem użyć jasnych nazw, takich jak publicSymbols, a nie moich zwykłych pubs lub anonimowych dosłownych obiektów. Możesz pisać o wiele bardziej zwięźle, jeśli chcesz mieć funkcje anonimowe, ale nie dbam o anonimowość funkcje .)

 85
Author: T.J. Crowder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-02 13:36:48

Istnieje kilka sposobów, aby to zrobić, to zależy, jaki jest twój cel, jeśli chcesz po prostu wykonać funkcję, a w tym samym kontekście, możesz użyć .apply():

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

Jeśli chcesz zastąpić go nowszym init, będzie wyglądał tak:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};
 56
Author: Nick Craver,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-02 13:07:15

Inne metody są świetne, ale nie zachowują żadnych prototypowych funkcji dołączonych do init. Aby to obejść, możesz wykonać następujące czynności (zainspirowane postem od Nicka Cravera).

(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
Author: Ally,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-10 09:49:50

Inną opcją może być:

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
Author: Sérgio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-06 15:20:00

Użyj extendFunction.js

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

Ale w twoim konkretnym przypadku łatwiej jest rozszerzyć globalną funkcję onload:

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

Naprawdę podoba mi się Twoje pytanie, to sprawia, że myślę o różnych przypadkach użycia.

Dla zdarzeń javascript, naprawdę chcesz dodawać i usuwać programy obsługi - ale dla funkcji extendFunction, jak możesz później usunąć funkcjonalność ? Mogę z łatwością dodać .przywróci metodę do funkcji rozszerzonych, więc init = init.revert() zwróci oryginał funkcja. Oczywiście może to prowadzić do jakiegoś dość złego kodu, ale być może Pozwala to zrobić coś bez dotykania zagranicznej części kodu.

 -2
Author: Devin G Rhode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-04 05:34:58