Переопределение функции JavaScript при ссылке на оригинал

У меня есть функция a(), которую я хочу переопределить, но также нужно, чтобы исходный a() выполнялся в порядке, зависящем от контекста. Например, иногда, когда я создаю страницу, я хочу переопределить вот так:

function a() {
    new_code();
    original_a();
}

а иногда так:

function a() {
    original_a();
    other_new_code();
}

Как я могу получить этот original_a() из a()? Это вообще возможно?

Пожалуйста, не предлагайте альтернативы перегрузке таким способом, я знаю многих. Я спрашиваю именно об этом.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
163
0
202 902
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Ответ принят как подходящий

Вы можете сделать что-то вроде этого:

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 24.07.2012 22:27

@Nerdmaster Спасибо, что указали на это. Я добавил примечание, чтобы, надеюсь, помочь людям заметить.

Matthew Crumley 25.07.2012 00:11

Вау, я попытался сделать это без пространства имен, и у меня переполнение стека, лол.

gosukiwi 10.08.2012 21:00

Я думаю, результат будет таким же, если убрать первую и последнюю круглые скобки из функции. Например, отсюда (functi.. и }). Простое выполнение }(); даст тот же результат, поскольку используется первый набор скобок (function{})(), потому что вы не можете просто объявить анонимное функциональное выражение, которое вы должны назначить ему. Но, делая (), вы ничего не определяете, просто возвращая функцию.

Muhammad Umer 04.08.2013 16:09

@MuhammadUmer Вы правы, что скобки вокруг выражения функции не нужны. Я включил их, потому что без них, если вы просто посмотрите на первые пару строк, это выглядит (по крайней мере, для меня), как будто вы назначаете внешнюю функцию напрямую a, а не вызываете ее и сохраняете возвращаемое значение. Скобки сообщают мне, что происходит еще кое-что.

Matthew Crumley 05.08.2013 16:59

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

Muhammad Umer 06.08.2013 01:52

Шаблон прокси может помочь вам:

(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.

верно. использование «применить» и «аргументы» делает это гораздо более надежным, чем другие ответы.

Robert Levy 21.05.2012 23:15

Обратите внимание, что этот шаблон не требует jQuery - они просто используют одну из функций jQuery в качестве примера.

Kev 01.04.2020 11:02

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

cellepo 20.02.2016 05:30

Вы можете переопределить функцию, используя такую ​​конструкцию:

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 Это намного более читабельно, чем другие примеры шаблонов прокси!

Mu Mind 26.05.2011 21:42

... но, если я не ошибаюсь, это ограничено функциями с нулевым аргументом или, по крайней мере, некоторым заранее определенным количеством аргументов. Есть ли способ обобщить его на произвольное количество аргументов без потери удобочитаемости?

Mu Mind 26.05.2011 21:48

@MuMind: да, но при правильном использовании шаблона прокси - см. мой ответ.

Dan Dascalescu 22.03.2014 06:10

Передача произвольных аргументов:

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?

Jonathan. 06.04.2013 13:42

В приведенных выше примерах неправильно применяется 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?

eitch 30.11.2016 14:06

Ответ, который дает @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 );
}

Здесь важны две вещи:

  1. Мы не используем стрелочную функцию () =>.

    Стрелочные функции изменяют ссылку на this, и нам нужно, чтобы это был this файла.

  2. Использование this.describe и this.describe.skip вместо только describe и describe.skip.

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

Другие вопросы по теме