У меня есть функция a(), которую я хочу переопределить, но также нужно, чтобы исходный a() выполнялся в порядке, зависящем от контекста. Например, иногда, когда я создаю страницу, я хочу переопределить вот так:
function a() {
new_code();
original_a();
}
а иногда так:
function a() {
original_a();
other_new_code();
}
Как я могу получить этот original_a() из a()? Это вообще возможно?
Пожалуйста, не предлагайте альтернативы перегрузке таким способом, я знаю многих. Я спрашиваю именно об этом.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вы можете сделать что-то вроде этого:
var a = (function() {
var original_a = a;
if (condition) {
return function() {
new_code();
original_a();
}
} else {
return function() {
original_a();
other_new_code();
}
}
})();
Объявление original_a внутри анонимной функции не дает ей загромождать глобальное пространство имен, но она доступна во внутренних функциях.
Как и Nerdmaster, упомянутый в комментариях, обязательно включите () в конце. Вы хотите вызвать внешнюю функцию и сохранить результат (одну из двух внутренних функций) в a, а не сохранять саму внешнюю функцию в a.
@Nerdmaster Спасибо, что указали на это. Я добавил примечание, чтобы, надеюсь, помочь людям заметить.
Вау, я попытался сделать это без пространства имен, и у меня переполнение стека, лол.
Я думаю, результат будет таким же, если убрать первую и последнюю круглые скобки из функции. Например, отсюда (functi.. и }). Простое выполнение }(); даст тот же результат, поскольку используется первый набор скобок (function{})(), потому что вы не можете просто объявить анонимное функциональное выражение, которое вы должны назначить ему. Но, делая (), вы ничего не определяете, просто возвращая функцию.
@MuhammadUmer Вы правы, что скобки вокруг выражения функции не нужны. Я включил их, потому что без них, если вы просто посмотрите на первые пару строк, это выглядит (по крайней мере, для меня), как будто вы назначаете внешнюю функцию напрямую a, а не вызываете ее и сохраняете возвращаемое значение. Скобки сообщают мне, что происходит еще кое-что.
это правда во время того, что я делал, после того, как я вернулся из потока своих мыслей, мне пришлось бы сделать мини-обзор того, что я делаю в коде, и очень запутался, почему эта функция назначается этому, а затем после прочтения построчно я бы увидел и запомнил о да ..
Шаблон прокси может помочь вам:
(function() {
// log all calls to setArray
var proxied = jQuery.fn.setArray;
jQuery.fn.setArray = function() {
console.info( this, arguments );
return proxied.apply( this, arguments );
};
})();
The above wraps its code in a function to hide the "proxied"-variable. It saves jQuery's setArray-method in a closure and overwrites it. The proxy then logs all calls to the method and delegates the call to the original. Using apply(this, arguments) guarantees that the caller won't be able to notice the difference between the original and the proxied method.
верно. использование «применить» и «аргументы» делает это гораздо более надежным, чем другие ответы.
Обратите внимание, что этот шаблон не требует jQuery - они просто используют одну из функций jQuery в качестве примера.
Спасибо, ребята, шаблон прокси действительно помог ..... На самом деле я хотел вызвать глобальную функцию foo .. На определенных страницах мне нужно сделать несколько проверок. Итак, я сделал следующее.
//Saving the original func
var org_foo = window.foo;
//Assigning proxy fucnc
window.foo = function(args){
//Performing checks
if (checkCondition(args)){
//Calling original funcs
org_foo(args);
}
};
Спасибо, это действительно помогло мне
Следуя образец прокси, в некоторых случаях будет важно реализовать эту деталь, которая отсутствует в коде этого ответа: вместо org_foo(args) вызовите org_foo.call(this, args). Это поддерживает this, как это было бы при обычном вызове window.foo (без проксирования). См. этот ответ
Вы можете переопределить функцию, используя такую конструкцию:
function override(f, g) {
return function() {
return g(f);
};
}
Например:
a = override(a, function(original_a) {
if (condition) { new_code(); original_a(); }
else { original_a(); other_new_code(); }
});
Обновлено: исправлена опечатка.
+1 Это намного более читабельно, чем другие примеры шаблонов прокси!
... но, если я не ошибаюсь, это ограничено функциями с нулевым аргументом или, по крайней мере, некоторым заранее определенным количеством аргументов. Есть ли способ обобщить его на произвольное количество аргументов без потери удобочитаемости?
@MuMind: да, но при правильном использовании шаблона прокси - см. мой ответ.
Передача произвольных аргументов:
a = override(a, function(original_a) {
if (condition) { new_code(); original_a.apply(this, arguments) ; }
else { original_a.apply(this, arguments); other_new_code(); }
});
разве аргументы не относятся к original_a?
В приведенных выше примерах неправильно применяется this или неправильно передается arguments в переопределение функции. Underscore _.wrap () обертывает существующие функции, применяет this и правильно передает arguments. См .: http://underscorejs.org/#wrap
У меня был код, написанный кем-то другим, и я хотел добавить строку к функции, которую я не мог найти в коде. Поэтому в качестве обходного пути я хотел отменить его.
Однако ни одно из решений не помогло мне.
Вот что сработало в моем случае:
if (typeof originalFunction === "undefined") {
originalFunction = targetFunction;
targetFunction = function(x, y) {
//Your code
originalFunction(a, b);
//Your Code
};
}
Я создал небольшой помощник для подобного сценария, потому что мне часто приходилось переопределять функции из нескольких библиотек. Этот помощник принимает «пространство имен» (контейнер функции), имя функции и замещающую функцию. Он заменит исходную функцию в указанном пространстве имен на новую.
Новая функция принимает исходную функцию в качестве первого аргумента, а аргументы исходной функции - в качестве остальных. Он всегда будет сохранять контекст. Он также поддерживает функции void и non-void.
function overrideFunction(namespace, baseFuncName, func) {
var originalFn = namespace[baseFuncName];
namespace[baseFuncName] = function () {
return func.apply(this, [originalFn.bind(this)].concat(Array.prototype.slice.call(arguments, 0)));
};
}
Использование, например, с Bootstrap:
overrideFunction($.fn.popover.Constructor.prototype, 'leave', function(baseFn, obj) {
// ... do stuff before base call
baseFn(obj);
// ... do stuff after base call
});
Однако я не создавал никаких тестов производительности. Это может добавить некоторые нежелательные накладные расходы, которые могут или не могут быть большой проблемой, в зависимости от сценария.
На мой взгляд, основные ответы не читаются / не обслуживаются, а другие ответы не связывают контекст должным образом. Вот удобочитаемое решение с использованием синтаксиса ES6 для решения обеих этих проблем.
const orginial = someObject.foo;
someObject.foo = function() {
if (condition) orginial.bind(this)(...arguments);
};
Но использование синтаксиса ES6 делает его довольно ограниченным в использовании. У вас есть версия, которая работает в ES5?
Ответ, который дает @Matthew Crumley, заключается в использовании немедленно вызываемых функциональных выражений, чтобы закрыть старую функцию 'a' в контексте выполнения возвращаемой функции. Я думаю, что это был лучший ответ, но лично я бы предпочел передать функцию «a» в качестве аргумента IIFE. Думаю, это более понятно.
var a = (function(original_a) {
if (condition) {
return function() {
new_code();
original_a();
}
} else {
return function() {
original_a();
other_new_code();
}
}
})(a);
Итак, мой ответ оказался решением, которое позволяет мне использовать переменную _this, указывающую на исходный объект. Я создаю новый экземпляр «Квадрата», но мне не понравилось то, как «Квадрат» генерировал его размер. Я думал, что это должно соответствовать моим конкретным потребностям. Однако для этого мне потребовалось, чтобы в квадрате была обновленная функция «GetSize» с внутренними компонентами этой функции, вызывающими другие функции, уже существующие в квадрате, такие как this.height, this.GetVolume (). Но для этого мне нужно было сделать это без каких-либо безумных хаков. Итак, вот мое решение.
Другой инициализатор объекта или вспомогательная функция.
this.viewer = new Autodesk.Viewing.Private.GuiViewer3D(
this.viewerContainer)
var viewer = this.viewer;
viewer.updateToolbarButtons = this.updateToolbarButtons(viewer);
Функция в другом объекте.
updateToolbarButtons = function(viewer) {
var _viewer = viewer;
return function(width, height){
blah blah black sheep I can refer to this.anything();
}
};
Не уверен, что это сработает при любых обстоятельствах, но в нашем случае мы пытались переопределить функцию describe в Jest, чтобы мы могли проанализировать имя и пропустить весь блок describe, если он соответствует некоторым критериям.
Вот что у нас сработало:
function describe( name, callback ) {
if ( name.includes( "skip" ) )
return this.describe.skip( name, callback );
else
return this.describe( name, callback );
}
Здесь важны две вещи:
Мы не используем стрелочную функцию () =>.
Стрелочные функции изменяют ссылку на this, и нам нужно, чтобы это был this файла.
Использование this.describe и this.describe.skip вместо только describe и describe.skip.
Опять же, не уверен, что это имеет значение для кого-либо, но мы изначально пытались обойтись с Мэтью Крамлиотличный ответ, но нам нужно было сделать наш метод функцией и принимать параметры, чтобы анализировать их в условном выражении.
Для любых идиотов вроде меня - обратите особое внимание на "()" в конце - без этого он возвращает внешнюю функцию, а не внутренние функции :)