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

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

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

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

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

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

Ответ 1

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

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

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

// 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 или анонимный объект. Вы можете записать его намного компактнее, если хотите иметь анонимные функции, но Мне не нравятся анонимные функции.)

Ответ 2

Есть несколько способов сделать это, это зависит от вашей цели, если вы просто хотите выполнить эту функцию, а также в том же контексте, вы можете использовать .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();
};

Ответ 3

Другие методы велики, но они не сохраняют никаких прототипов, связанных с 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;
}) ();

Ответ 4

Другой вариант:

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

Ответ 5

Используйте extendFunction.js

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

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

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

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

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