Основная причина, по которой я хочу это, заключается в том, что я хочу расширить свою функцию инициализации.
Что-то вроде этого:
// main.js
window.onload = init();
function init(){
doSomething();
}
// extend.js
function extends init(){
doSomethingHereToo();
}
Поэтому я хочу расширить функцию, как расширяю класс в PHP.
И я хотел бы расширить его и из других файлов, поэтому, например, у меня есть исходная функция init main.js
и расширенная функция extended.js
.
javascript
function
extend
Адам Халаш
источник
источник
Ответы:
Я уверен, что с более широким взглядом на то, что вы на самом деле пытаетесь сделать, и на контекст, в котором вы это делаете, мы могли бы дать вам лучший ответ, чем буквальный ответ на ваш вопрос.
Но вот дословный ответ:
Если вы где-то назначаете эти функции какому-либо свойству, вы можете обернуть исходную функцию и вместо этого поместить свою замену в свойство:
// 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 = { init: function init() { } }; // In extended.js (function() { var oldInit = MyLibrary.init; MyLibrary.init = extendedInit; function extendedInit() { oldInit.call(MyLibrary); // Use #call in case `init` uses `this` doSomething(); } })();
Но есть способы сделать это лучше. Как, например, предоставление средств регистрации
init
функций.// In main.js var MyLibrary = (function() { var initFunctions = []; return { init: function init() { var fns = initFunctions; initFunctions = undefined; for (var index = 0; index < fns.length; ++index) { try { fns[index](); } catch (e) { } } }, addInitFunction: function addInitFunction(fn) { if (initFunctions) { // Init hasn't run yet, remember it initFunctions.push(fn); } else { // `init` has already run, call it almost immediately // but *asynchronously* (so the caller never sees the // call synchronously) setTimeout(fn, 0); } } }; })();
Здесь, в 2020 году (или в любое время после ~ 2016 года), это можно записать более компактно:
// In main.js const MyLibrary = (() => { let initFunctions = []; return { init() { const fns = initFunctions; initFunctions = undefined; for (const fn of fns) { try { fn(); } catch (e) { } } }, addInitFunction(fn) { if (initFunctions) { // Init hasn't run yet, remember it initFunctions.push(fn); } else { // `init` has already run, call it almost immediately // but *asynchronously* (so the caller never sees the // call synchronously) setTimeout(fn, 0); // Or: `Promise.resolve().then(() => fn());` // (Not `.then(fn)` just to avoid passing it an argument) } } }; })();
источник
Есть несколько способов сделать это, это зависит от вашей цели, если вы просто хотите выполнить функцию и в том же контексте, вы можете использовать
.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(); };
источник
.call
метод вместо.apply
. См. Этот вопрос на StackOverflow.Другие методы хороши, но они не сохраняют никаких функций-прототипов, прикрепленных к 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 );
источник
2017+ решение
Идея расширения функций исходит из функциональной парадигмы, которая изначально поддерживается начиная с ES6:
function init(){ doSomething(); } // extend.js init = (f => u => { f(u) doSomethingHereToo(); })(init); init();
Согласно озабоченности @TJCrowder дампом стека, браузеры сегодня справляются с ситуацией намного лучше. Если вы сохраните этот код в test.html и запустите его, вы получите
test.html:3 Uncaught ReferenceError: doSomething is not defined at init (test.html:3) at test.html:8 at test.html:12
Строка 12: вызов инициализации, Строка 8: добавочный номер инициализации, Строка 3: неопределенный
doSomething()
вызов.Примечание: большое уважение ветерану TJ Crowder, который любезно ответил на мой вопрос много лет назад, когда я был новичком. Спустя годы я до сих пор помню уважительное отношение и стараюсь следовать хорошему примеру.
источник
Это очень просто и понятно. Посмотрите на код. Попытайтесь понять основную концепцию расширения 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 }
Теперь вы можете использовать функцию Child следующим образом:
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 } }
Давайте расширим этот класс функцией Child следующим образом:
function Child(props) { let parent = new BaseClass(props) const getMessage = () => `Message is ${parent.getProps()}`; return { ...parent, getMessage} // I have used spread operator. }
Опять же, вы можете использовать функцию Child следующим образом, чтобы получить аналогичный результат:
let childObject = Child("Secret Message") console.log(childObject.getMessage()) // logs "Message is Secret Message" console.log(childObject.getProps()) // logs "Secret Message"
Javascript - очень простой язык. Мы можем делать почти все. Удачного JavaScripting ... Надеюсь, я смог дать вам идею для использования в вашем случае.
источник
Используйте extendFunction.js
init = extendFunction(init, function(args) { doSomethingHereToo(); });
Но в вашем конкретном случае проще расширить глобальную функцию onload:
extendFunction('onload', function(args) { doSomethingHereToo(); });
Мне действительно нравится ваш вопрос, он заставляет задуматься о различных вариантах использования.
Для событий javascript вы действительно хотите добавлять и удалять обработчики, но для extendFunction как вы могли бы позже удалить функциональность? Я мог бы легко добавить метод .revert к расширенным функциям, чтобы
init = init.revert()
вернуть исходную функцию. Очевидно, что это может привести к довольно плохому коду, но, возможно, это позволит вам что-то сделать, не затрагивая чужую часть кодовой базы.источник