Подтвердить что ты не робот

Легитимное использование конструктора Function

Как неоднократно говорилось, считается неправильной практикой использовать конструктор функций (также см. Спецификация языка ECMAScript, 5 th edition, § 15.3.2.1):

new Function ([arg1[, arg2[, … argN]],] functionBody)

(где все аргументы - это строки, содержащие имена аргументов, а последняя (или только) строка содержит тело функции).

Чтобы повторить, говорят, что он медленный, как объясняется команда Opera:

Каждый раз [...] Functionконструктор вызывается в строке представляющий исходный код, scriptдвигатель должен запустить оборудование, которое конвертирует исходный код в исполняемый файл код. Обычно это дорого производительность - легко сто раз более дорогая, чем простая функция звоните, например. (Марк Тарквин Уилтон-Джонс)

Хотя это не так уж плохо, согласно этот пост на MDC (я сам не тестировал это, используя текущую версию Firefox, хотя).

Crockford добавляет, что

[t] он цитирует условные обозначения языка очень сложно правильно выразить тело функции как строка. В строковой форме, рано проверка ошибок не может быть выполнена. […] А также это расточительство памяти, потому что каждый функция требует наличия собственных независимых реализация.

Другое отличие состоит в том, что

функция, определенная функцией конструктор не наследует никакой области кроме глобального масштаба (все функции наследуют). (MDC)

Кроме того, вы должны быть внимательны, чтобы избежать инъекции вредоносного кода при создании new Function с использованием динамического содержимого.

Тем не менее, T.J. Кроудер говорит в ответ, который

[t] здесь почти никогда не требуется аналогичная [...] новая функция (...), либо, за исключением некоторых продвинутых краевые случаи.

Итак, теперь мне интересно: что это за "передовые случаи края"? Есть ли законное использование конструктора Function?

4b9b3361

Ответ 1

NWMatcher - Селектор и сопоставление CSS для JavaScript от Diego Perini - использует конструктор Function (1, 2, 3, 4 и т.д.) для создания ( "компиляции" ) высокоэффективные версии селекторных соединителей.

Тест (который я только что запускал в Chrome 5) говорит сам за себя:

alt text

Обратите внимание на разницу между NWMatcher и Sizzle, которая является очень похожим движком выбора, только без компиляции функций:)

С другой стороны, ECMAScript 5 не вызывает ошибок при вызове Function. Ни в строгом, ни в "стандартном" режимах. Строгий режим, однако, вводит несколько ограничений на присутствие идентификаторов, таких как "eval" и "arguments":

  • Вы не можете объявлять переменные/функции/аргументы с такими именами:

    function eval() { }
    var eval = { };
    function f(eval) { } 
    var o = { set f(eval){ } };
    
  • Вы не можете назначить такой идентификатор:

    eval = { };
    

Также обратите внимание, что в строгом режиме семантика eval немного отличается от семантики в ES3. Строковый код режима не может создавать переменные или функции в среде, из которой он был вызван:

 eval(' "use strict"; var x = 1; ');
 typeof x; // "undefined"

Ответ 2

jQuery использует его для разбора строк JSON, когда объект парсера JSON недоступен. Кажется законным для меня:)

        // Try to use the native JSON parser first
        return window.JSON && window.JSON.parse ?
            window.JSON.parse( data ) :
            (new Function("return " + data))();

Ответ 3

Я использую конструктор new Function() в качестве встроенного JS-интерпретатора в одном из веб-приложений, которые я разрабатываю:

function interpret(s) {
  //eval(s); <-- even worse practice
  try {
      var f = new Function(s);
      f();
    }
  catch (err) {
      //graceful error handling in the case of malformed code
  }
}

По мере того как я получаю потоковое вещание по AJAX ( не iframe), я непрерывно interpret() его на readyStateChange == 3. Это работает на удивление хорошо.

Изменить: здесь приведено четкое исследование, в котором показано, что new Function() категорически быстрее, чем eval(). То есть вы никогда не должны (редко?) использовать eval вместо new Function().

http://polyfx.com/stuff/bsort.html < - версия итерации 1000, может привести к сбою вашего браузера.

http://polyfx.com/stuff/bsort10.html < - более короткая версия

Eval в среднем, почти 8 раз медленнее, чем new Function().

Ответ 4

Джон Ресиг использовал конструктор Function для создания "скомпилированных" версий клиентских шаблонов, написанных в синтаксисе asp. http://ejohn.org/blog/javascript-micro-templating/

Ответ 5

Это отдельный случай из моего другого ответа.

Я снова использовал конструктор Function, чтобы создать настраиваемые форматированные строки, которые вызывались повторно. Накладные расходы на создание функции (которая, как я понимаю, проблема с производительностью, о которой вы говорите) были намного перевешимы благодаря улучшенной производительности настраиваемых функций, которые были созданы во время выполнения специально для обработки определенной строки формата, и, следовательно, не нужно было оценивать тонны нерелевантных случаев - или анализировать строку формата, если на то пошло. Это немного похоже на компиляцию регулярного выражения, я полагаю.

Ответ 6

Единственное законное использование, к которому я пришел, - это когда я написал это:

Function.prototype.New = (function () {
  var fs = [];
  return function () {
    var f = fs [arguments.length];
    if (f) {
      return f.apply (this, arguments);
    }
    var argStrs = [];
    for (var i = 0; i < arguments.length; ++i) {
      argStrs.push ("a[" + i + "]");
    }
    f = new Function ("var a=arguments;return new this(" + argStrs.join () + ");");
    if (arguments.length < 100) {
      fs [arguments.length] = f;
    }
    return f.apply (this, arguments);
  };
}) ();

Код позволяет использовать Function.prototype.apply при использовании 'ключевого слова new.

Пример:

function Foo (x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
  this.otherArgs = Array.prototype.slice.call (arguments, 3);
}

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7);
var bool = true
  && foo.x == 1
  && foo.y == 2
  && foo.z == 3
  && foo.otherArgs.length == 4
  && foo.otherArgs [0] == 4
  && foo.otherArgs [1] == 5
  && foo.otherArgs [2] == 6
  && foo.otherArgs [3] == 7
  ;

alert (bool);

Ответ 7

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

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

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