Можете ли вы изменить функцию Javascript после ее объявления? - PullRequest
57 голосов
/ 26 января 2010

Допустим, у меня есть var a = function() { return 1; }. Можно ли изменить a, чтобы a() вернул 2? Возможно, редактируя свойство объекта a, поскольку каждая функция является объектом ?

Обновление: Ух, спасибо за все ответы. Тем не менее, я боюсь, что я не хотел просто переназначить переменную, а на самом деле редактировать существующую функцию. Я думаю о том, как можно объединить частичные функции в Scala для создания нового PartialFunction. Я заинтересован в написании чего-то подобного в Javascript и думал, что существующая функция может быть обновлена, а не создан совершенно новый объект Function.

Ответы [ 13 ]

39 голосов
/ 26 января 2010

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

var a = function(){ return 1; }

alert(a()); //1

// keep a reference
var old = a;

// redefine
a = function(){
  // call the original function with any arguments specified, storing the result
  var originalResult = old.apply(old, arguments);
  // add one
  return originalResult + 1;
};
alert(a()); //2

Вуаля.

Редактировать: Обновлено, чтобы показать это в безумном сценарии:

var test = new String("123");
console.log(test.toString()); // logs 123
console.log(test.substring(0)); // logs 123
String.prototype.substring = function(){ return "hahanope"; }
console.log(test.substring(0)); // logs hahanope

Здесь вы можете видеть, что, хотя сначала определяется «test», а мы переопределяем substring (), изменение все равно применяется.

Примечание: вы действительно должны пересмотреть свою архитектуру, если вы делаете это ... вы будете путать дерьмо с каким-то плохим разработчиком через 5 лет, когда он / она смотрит на определение функции, которое должно вернуть 1, но, кажется, всегда возвращает 2 ....

39 голосов
/ 27 января 2010

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

// declare function foo
var foo = function (a) { alert(a); };

// modify function foo
foo = new Function (
  "a",
  foo.toSource()
    .replace("alert(a)", "alert('function modified - ' + a)")
    .replace(/^function[^{]+{/i,"")  // remove everything up to and including the first curly bracket
    .replace(/}[^}]*$/i, "")  // remove last curly bracket and everything after<br>
);

Вместо toSource () вы, вероятно, можете использовать toString () , чтобы получить строку, содержащую объявление функции. Некоторые вызовы метода replace () для подготовки строки для использования с конструктором функций и изменения источника функции.

22 голосов
/ 26 января 2010
var a = function() { return 1; }
alert(a()) // 1
a = function() { return 2; }
alert(a()) // 2

технически вы теряете одно определение функции и заменяете его другим.

15 голосов
/ 02 июля 2014

Таким образом, вы хотите изменить код функции непосредственно, на месте, а не просто переназначить другую функцию существующей переменной.

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

В документации для MDN перечислены свойства и методы объекта функции. Ни один из них не дает нам возможности манипулировать телом функции извне.

Это потому, что в соответствии со спецификацией , тело функции хранится во внутреннем свойстве [[Code]] объекта функции, к которому нет прямого доступа.

15 голосов
/ 26 января 2010

Как насчет этого, без необходимости переопределять функцию:

var a = function() { return arguments.callee.value || 1; };
alert(a()); // => 1
a.value = 2;
alert(a()); // => 2
11 голосов
/ 16 мая 2013

Я придерживаюсь решения jvenema, в котором мне не нравится глобальная переменная "old". Кажется, лучше сохранить старую функцию внутри новой:

function a() { return 1; }

// redefine
a = (function(){
  var _a = a;
  return function() {
  // You may reuse the original function ...
  // Typical case: Conditionally use old/new behaviour
    var originalResult = _a.apply(this, arguments);
  // ... and modify the logic in any way
    return originalResult + 1;
    }
})();
a()  // --> gives 2
3 голосов
/ 17 декабря 2014

Все выполнимые решения придерживаются «подхода оборачивания функций». Самым надежным среди них, по-видимому, является rplantiko .

Такая функция упаковки легко может быть абстрагирована. Концепция / шаблон Сам по себе может быть назван «Модификация метода». Его реализация определенно принадлежит Function.prototype. Было бы неплохо быть поддержанным один день стандартными модификаторами прототипа, такими как <a href="https://gist.github.com/petsel/37bf42a6fc0b7f9549b8" rel="nofollow noreferrer">before</a>, <a href="https://gist.github.com/petsel/21677f85aa598d262c66" rel="nofollow noreferrer">after</a>, <a href="https://gist.github.com/petsel/895cd9eb04239e10e8eb" rel="nofollow noreferrer">around</a>, <a href="https://gist.github.com/petsel/9ddf5ce515f1344c84bd" rel="nofollow noreferrer">afterThrowing</a> и <a href="https://gist.github.com/petsel/3b31cd5e63d43b9e7c4e" rel="nofollow noreferrer">afterFinally</a>.

Что касается вышеупомянутого примера от rplantiko ...

function a () { return 1; }

// redefine
a = (function () {
  var _a = a;
  return function () {
    // You may reuse the original function ...
    // Typical case: Conditionally use old/new behaviour
    var originalResult = _a.apply(this, arguments);
    // ... and modify the logic in any way
    return originalResult + 1;
  };
})();

a(); // --> gives 2

... и используя [around], код преобразуется в ...

function a () { return 1; }

console.log("a : ", a);
console.log("a() : ", a());


a = a.around(function (proceed, interceptor, args) {
  return (proceed() + 1);
});

console.log("a : ", a);
console.log("a() : ", a());
0 голосов
/ 16 января 2018

Вы можете изменять функции, как и другие объекты

var a1 = function(){return 1;}
var b1 = a1;
a1 = function(){
  return b1() + 1;
};
console.log(a1()); // return 2

// OR:
function a2(){return 1;}
var b2 = a2;
a2 = function(){
  return b2() + 1;
};
console.log(a2()); // return 2
0 голосов
/ 23 сентября 2017
                    function get_func(func) {//from StackOverflow
                        //var args = Array.prototype.slice.call(arguments);
                        var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
                        var ARGUMENT_NAMES = /([^\s,]+)/g;
                      var fnStr = func.toString().replace(STRIP_COMMENTS, '');
                      var args = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
                      //var code = func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1];
                      var code =fnStr.slice(fnStr.indexOf("{") + 1, fnStr.lastIndexOf("}"));
                        var func_full_name=fnStr.substr(0,  fnStr.indexOf('{') );
                        var func_name=fnStr.slice(9, fnStr.indexOf('('));
                        if(args === null){ args = []; }
                      return { func_name:func_name,func_full_name:func_full_name, code:code, args:args};
                    }
                    function str_trim(str){
                        //replaces multi-line characters
                        return str.replace(/\n/g,'').replace(/^ +| +$/gm, '').trim();
                    }
                    function edit_func(func,callback){
                        var a_func=get_func(func);
                        var lines = a_func.code.split('\n');
                        var output=[];
                        for(var i=0;i<lines.length;i++){
                            var code=str_trim(lines[i]);
                            if(code!=''){
                                code =callback(code,output.length,a_func.args);
                                output.push(code);
                            }
                        }
                        //var test=Function(['a','b'],'alert(a);alert(b);');
                        //test(1,2);
                        //func=Function(a_func.args,output.join('\n'));
                        //return func;
                         var head = document.getElementsByTagName('head')[0];
                         var script = document.createElement('script');
                         script.type = 'text/javascript';
                        script.text =a_func.func_full_name+'{'+ output.join('\n') + '}'  ;
                        head.appendChild(script);
                    }
                    function test(id) {
                         var x, y;
                         x = 5;
                         y = 10;
                         alert(x + y);
                         alert(id);
                     }
                     edit_func(test,function(code,line,args){
                        if(line==3){
                            return 'alert(x*y);';
                        }
                        return code;
                    });
                        test(3);

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

0 голосов
/ 22 октября 2013

Это Пример сброса , основанный на таймере управления eworld.ui www.eworldui.net

Имея TimePicker eworld.ui, где JavaScript недоступен извне, вы не можете найти js, связанные с этими элементами управления. Итак, как вы можете добавить событие onchange в указатель времени?

Существует функция js, вызываемая при Select времени между всеми параметрами, которые предлагает элемент управления. Эта функция: TimePicker_Up_SelectTime

Сначала вы должны скопировать код внутри этой функции.

Оценка ... quikwatch ... TimePicker_Up_SelectTime.toString()

function TimePicker_Up_SelectTime(tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) {
    document.getElementById(tbName).value = selTime;
    if(lblName != '')
    document.getElementById(lblName).innerHTML = selTime;
    document.getElementById(divName).style.visibility = 'hidden';
    if(enableHide)
    TimePicker_Up_ShowHideDDL('visible');
    if(customFunc != "")
    eval(customFunc + "('" + selTime + "', '" + tbName + "');");
    eval(postbackFunc + "();");

}

Теперь

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

TimePicker_Up_SelectTime = функция (tbName, lblName, divName, selTime, enableHide, postbackFunc, customFunc) { document.getElementById (tbName) .value = selTime; if (lblName! = '') document.getElementById (lblName) .innerHTML = selTime; document.getElementById (divName) .style.visibility = 'hidden'; if (enableHide) TimePicker_Up_ShowHideDDL ( 'видимого'); if (customFunc! = "") eval (customFunc + "('" + selTime + "', '" + tbName + "');"); eval (postbackFunc + "();");

  >>>>>>>  My function  >>>>>   RaiseChange(tbName);
}

Я добавил мою функцию в функцию, чтобы теперь я мог имитировать событие onchange при выборе времени.

RaiseChange (...) может быть любым, что вы хотите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...