Javascript: расширение функции


основная причина, почему я хочу это, что я хочу, чтобы расширить мою функцию инициализации.

что-то вроде этого:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

поэтому я хочу расширить функцию, как я расширяю класс в PHP.

и я хотел бы расширить его из других файлов тоже, так что, например, у меня есть исходная функция init в main.js и расширенная функция в extended.js.

5 73

5 ответов:

С более широким представлением о том, что вы на самом деле пытаетесь сделать, и контекст, в котором вы это делаете, я уверен, что мы могли бы дать вам лучший ответ, чем литерал ответ на ваш вопрос.

но вот буквальный ответ:

если вы назначаете эти функции какому-то свойству где-то, вы можете обернуть исходную функцию и поместить свою замену в свойство вместо этого:

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

если ваши функции еще не находятся на объекте, вы, вероятно, захотите поместить их туда, чтобы облегчить вышеизложенное. Например:

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

но есть такие лучшие способы сделать это. Например, предоставление средств регистрации 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;
})();

(многое из вышеперечисленного можно было бы написать немного более компактно, но я хотел использовать четкие имена, такие как publicSymbols а не мой обычный pubs анонимный объект или литерал. Вы можете написать его намного компактнее, если хотите иметь анонимные функции, но я не очень люблю анонимные функции.)

есть несколько способов сделать это, это зависит от того, какова ваша цель, если вы просто хотите выполнить функцию, а также и в том же контексте, вы можете использовать .apply():

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

если вы хотите заменить его на более новые init, это будет выглядеть так:

function init(){
  doSomething();
}
//anytime later
var old_init = init;
init = function() {
  old_init.apply(this, arguments);
  doSomethingHereToo();
};

другие методы великолепны, но они не сохраняют никаких функций прототипа, прикрепленных к init. Чтобы обойти это, вы можете сделать следующее (вдохновленный сообщением от Ника Крейвера).

(function () {
    var old_prototype = init.prototype;
    var old_init = init;
    init = function () {
        old_init.apply(this, arguments);
        // Do something extra
    };
    init.prototype = old_prototype;
}) ();

другой вариант может быть:

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

использовать extendFunction.js

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

но в вашем конкретном случае, проще расширить глобальную функцию onload:

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

мне на самом деле очень нравится ваш вопрос, это заставляет меня думать о различных случаях использования.

для событий javascript вы действительно хотите добавлять и удалять обработчики - но для extendFunction, как вы могли бы позже удалить функциональность? Я мог бы легко добавить .верните метод к расширенным функциям, поэтому init = init.revert() вернет исходную функцию. Очевидно, что это может привести к некоторому довольно плохому коду, но, возможно, это позволит вам что-то сделать, не касаясь чужой части кодовой базы.