Javascript: расширение функции - PullRequest
79 голосов
/ 02 января 2011

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

Примерно так:

// main.js

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

// extend.js

function extends init(){
    doSomethingHereToo();
}

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

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

Ответы [ 6 ]

90 голосов
/ 02 января 2011

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

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

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

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

58 голосов
/ 02 января 2011

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

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

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

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 );
0 голосов
/ 12 марта 2019

Это очень просто и понятно.Посмотри на код.Попытайтесь понять базовую концепцию, лежащую в основе расширения javascript.

Сначала давайте расширим функцию javascript.

function Base(props) {
    const _props = props
    this.getProps = () => _props

    // We can make method private by not binding it to this object. 
    // Hence it is not exposed when we return this.
    const privateMethod = () => "do internal stuff" 

    return this
}

Вы можете расширить эту функцию, создав дочернюю функцию следующим образом

function Child(props) {
    const parent = Base(props)
    this.getMessage = () => `Message is ${parent.getProps()}`;

    // You can remove the line below to extend as in private inheritance, 
    // not exposing parent function properties and method.
    this.prototype = parent
    return this
}

Теперь вы можете использовать дочернюю функцию следующим образом:

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

Мы также можем создать функцию Javascript, расширив классы Javascript следующим образом.

class BaseClass {
    constructor(props) {
        this.props = props
        // You can remove the line below to make getProps method private. 
        // As it will not be binded to this, but let it be
        this.getProps = this.getProps.bind(this)
    }

    getProps() {
        return this.props
    }
}

Давайте расширим этот класс с помощьюДочерняя функция, подобная этой,

function Child(props) {
    let parent = new BaseClass(props)
    const getMessage = () => `Message is ${parent.getProps()}`;
    return { ...parent, getMessage} // I have used spread operator. 
}

Опять же, вы можете использовать дочернюю функцию следующим образом, чтобы получить похожий результат,

let childObject = Child("Secret Message")
console.log(childObject.getMessage())     // logs "Message is Secret Message"
console.log(childObject.getProps())       // logs "Secret Message"

Javascript - очень простой язык.Мы можем сделать что угодно.Удачной JavaScripting ... Надеюсь, я смог дать вам идею использовать в вашем случае.

0 голосов
/ 04 апреля 2013

Использование exteFunction.js

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

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

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

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

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

...