Изменить поведение функции без прокси

Я могу сделать что-то вроде этого:

var foo = ...// some function assignment
var fooString = foo.toString()
...
// add some alert to foo
...
var fooWithAlert = new Function(forStringWithAlert)

Есть ли способ изменить первый foo вместо создания новой функции? Мне нужно, чтобы обезьяна исправила некоторую зависимость, не воссоздавая всю иерархию объектов.

Мне нужно исправить функцию конструктора в библиотеке, просто добавляйте оповещение при каждом вызове. Но без жонглирования прототипами


person kharandziuk    schedule 22.02.2016    source источник
comment
вы можете манипулировать строкой, добавив функцию оповещения, а затем добавив эту обработанную строку в тег заголовка, не уверен в других решениях.   -  person Rajshekar Reddy    schedule 22.02.2016
comment
Хорошо упрощать, но я думаю, что вы зашли так далеко, упрощая этот вопрос, что, вероятно, пропустили детали, относящиеся к ответам. Что конкретно вам нужно сделать с функцией, на которую ссылается foo? Можете ли вы придумать простую, но репрезентативную вещь, которую вам нужно сделать?   -  person T.J. Crowder    schedule 22.02.2016
comment
Возможный дубликат Javascript: расширение функции   -  person gurvinder372    schedule 22.02.2016


Ответы (1)


Нет, вы не можете изменить функцию, на которую ссылается foo. Вы можете сделать так, чтобы foo ссылалась только на новую функцию, которая делает то, что вы хотите. Один из способов сделать это — использовать ваш toString подход, но лучше избегать этого, если это вообще возможно, потому что функция, которую вы получите в результате, не будет такой же, как оригинал; область, к которой он имеет доступ, будет другой.

Обычно вам нужен прокси/оболочка, например:

// The original foo
var foo = function(arg) {
    return "original foo says '" + arg + "'";
};
console.log(foo("bar"));

// Let's wrap it
(function() {
    var originalFoo = foo;
    foo = function() {
        return originalFoo.apply(this, arguments) + " plus updated foo";
    };
})();
console.log(foo("bar"));

Это не создает иерархию объектов или что-то подобное, оно просто обертывает foo.

Если foo является функцией-конструктором (назовем ее Foo), вы также захотите скопировать Foo.prototype:

// The original Foo
var Foo = function(arg) {
    this.value = "original foo";
    this.arg = arg;
};
Foo.prototype.getArg = function() {
    return this.arg;
};
var f1 = new Foo("bar");
console.log(f1.getArg());

// Let's wrap it
(function() {
    var originalFoo = Foo;
    Foo = function() {
        var rv = originalFoo.apply(this, arguments);
        this.arg += " (plus more from augmented foo)";
        return rv;
    };
    Foo.prototype = originalFoo.prototype;
})();
var f2 = new Foo("bar");
console.log(f2.getArg());

И, конечно же, если вам нужно обернуть функцию на Foo.prototype, вы можете сделать это так же, как foo в моем первом примере:

// The original Foo
var Foo = function(arg) {
    this.value = "original foo";
    this.arg = arg;
};
Foo.prototype.getArg = function() {
    return this.arg;
};
var f = new Foo("bar");
console.log(f.getArg());

// Let's wrap its getArg
(function() {
    var originalGetArg = Foo.prototype.getArg;
    Foo.prototype.getArg = function() {
        return originalGetArg.apply(this, arguments) + " updated";
    };
})();
console.log(f.getArg());

Обратите внимание, что не имеет значения, что мы обернули функцию-прототип после создания объекта f.

person T.J. Crowder    schedule 22.02.2016
comment
возможно, лучше инкапсулировать originalFoo в IIFE? - person Alnitak; 22.02.2016
comment
что, если он захочет добавить в него каких-то 20 строк логики кода? Можно ли этого добиться этим методом? - person Rajshekar Reddy; 22.02.2016
comment
Думаю, мне нужно быть более конкретным github.com/Jxck/assert/ blob/master/assert.js#L190 Что происходит с прототипом foo в этом случае? - person kharandziuk; 22.02.2016
comment
@Reddy: Да, количество не имеет значения, важно только место. Обертки работают для того, чтобы делать что-то до оригинала, или после, или и то, и другое, но не посередине. - person T.J. Crowder; 22.02.2016
comment
@Alnitak: я не ожидал, что ОП будет использовать код дословно, я ожидал здравого смысла. :-) - person T.J. Crowder; 22.02.2016
comment
@kharandziuk: Детали, относящиеся к вашему вопросу, должны быть в вашем вопросе, а не просто по ссылке. - person T.J. Crowder; 22.02.2016
comment
@kharandziuk: я обновил ответ, включив примечание о функциях конструктора и функциях объекта prototype конструктора. - person T.J. Crowder; 22.02.2016
comment
одна небольшая ошибка (обнаруженная во время игры с аналогичной системой) заключается в том, что замененная функция не будет иметь ту же арность, что и оригинал. Я не знаю, можно ли просто сказать Foo.length = originalFoo.length - person Alnitak; 22.02.2016
comment
@Alnitak: заменяемая функция не будет иметь ту же арность, что и исходная Хорошая мысль! Я не знаю, допустимо ли просто сказать Foo.length = originalFoo.length Нет, length в функциях доступно только для чтения в соответствии со спецификацией. Редко, конечно, что-то действительно проверяет арность функции, но, тем не менее, это хороший момент. Если бы мне это было нужно, я бы, вероятно, просто создал ветку и создал нужную мне арность (скажем, до 10 и используя new Function выше этого)... - person T.J. Crowder; 22.02.2016
comment
Да, я попробовал и обнаружил, что f.length действительно доступен только для чтения. Недавно я видел код, который проверяет арность переданной функции, но я не помню, что это было. - person Alnitak; 22.02.2016