функция декоратора, переназначающая поднятую внутреннюю функцию без оценки

#javascript #eval #decorator #hoisting

#javascript #оценка #декоратор #подъем

Вопрос:

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

 var __slice = [].slice;

  this.around = function(decoration) {
    return function(base) {
      return function() {
        var argv, callback, __value__,
          _this = this;
        argv = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        __value__ = void 0;
        callback = function() {
          return __value__ = base.apply(_this, argv);
        };
        decoration.apply(this, [callback].concat(argv));
        return __value__;
      };
    };
  };

f("from outer");
function f(a){
  console.log("f OUTER",a);
};
f("from outer");
(function g(){
  f("from Inner (hoisted)");


function addDecorator(decoration, fn) {
  var decorated = around(decoration)(fn);
  eval(fn.name   " = decorated");
  decorated.unDecorate = function (){
    eval(fn.name   " = fn");
  }
}

  function myDecorator(cb){
      console.log("before");
      cb();
      console.log("after");
    }


  function f(a){
    console.log("f INNER",a);
  }

  f("from Inner (after declaration)");
  addDecorator(myDecorator, f);

  f("from Inner (Decorated)");
  f.unDecorate();
  f("from Inner (Undecorated)");
})();
f("from outer");
  

Я хочу иметь возможность вызывать addDecorator где угодно внутри g . т. е. вверху, где f он поднят над его объявлением.

В консоли Chrome я получаю следующий вывод:

 f OUTER from outer
f OUTER from outer
f INNER from Inner (hoisted)
f INNER from Inner (after declaration)
before
f INNER from Inner (Decorated)
after
f INNER from Inner (Undecorated)
f OUTER from outer 
  
  1. Можно ли это сделать без eval?
  2. Можно ли переместить addDecorator за пределы g? (не так важно)

Комментарии:

1. Я не совсем понимаю, о чем вы спрашиваете, но если главный вопрос — могу ли я вызвать decorated f поверх g до того, как к нему было применено оформление f — мой ответ нет. Подъем выводит тела функций для использования выше в коде, чем те функции, которым они назначены, но оформление — это операция, а не объявление, поэтому это произойдет только в данный момент в последовательности выполнения.

2. Нет, вопрос не в этом. Это очень интересный способ переопределения функций — и кажется, что ответы на оба вопроса на самом деле «нет». Тем не менее, я все еще исследую.)

3. @Brock, f поднимается до вершины g, в g добавляется украшение, затем объявляется f . Я не ожидал, что смогу вызвать decorated f перед вызовом функции декоратора.

4. Я приму ответы, которые изменяют g структуру или вызываются, если addDecorator можно узнать, где определена переданная функция, поэтому ей не нужно возвращать оформленную функцию, которая будет присвоена имени исходной функции. т. Е. Я не хочу иметь подобный код f = addDecorator(d,f) , поэтому я могу сделатьвариационная версия: addDecorationXxToAllOf(f1, f2, f3)

5. @SamHasler: ответ определенно «нет» на оба вопроса. В отличие от глобальной области видимости, JavaScript явно не дает вам способа доступа к области видимости функции каким-либо общим способом. Таким образом, вы можете либо вернуть новое значение функции, либо использовать объект для хранения функций. Обратите внимание, что instrumentFunction вышеописанное также работает только для глобальных функций, к которым можно получить доступ.

Ответ №1:

Вы можете как исключить eval , так и переместить функцию за пределы g , если хотите немного изменить свое соглашение.

Это будет иметь тот же эффект, но вызывается немного по-другому:

 function addDecorator(decoration, fn) {
    var decorated = around(decoration)(fn);
    decorated.unDecorate = function () {
        return fn;
    }
    return decorated;
}

f = addDecorator(myDecorator, f);

f = f.unDecorate();
  

http://jsfiddle.net/8SuT9/1/

Если вы не хотите менять способ ее вызова / использования, то, боюсь, ответ на оба ваших вопроса — нет.

Комментарии:

1. Я думаю, что я был бы более готов поместить все свои функции внутрь объекта, чтобы я мог выполнить передачу объекта в addDecorator, чтобы избежать необходимости выполнять назначение.

2. @SamHasler также выполнимо. Так или иначе, я полагаю, вам придется изменить свой подход, чтобы добиться удаления eval .