Законное использование конструктора Function - PullRequest
40 голосов
/ 12 июня 2010

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

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

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

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

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

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

Крокфорд добавляет , что

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

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

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

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

Тем не менее, T.J. Краудер говорит в ответ , что

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

Итак, теперь мне интересно: что это за «передовые крайние случаи»? Существуют ли допустимые варианты использования конструктора Function?

Ответы [ 7 ]

19 голосов
/ 12 июня 2010

NWMatcher - Javascript CSS селектор и сопоставитель, Диего Перини - использует конструктор 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"
9 голосов
/ 12 июня 2010

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

        // Try to use the native JSON parser first
        return window.JSON && window.JSON.parse ?
            window.JSON.parse( data ) :
            (new Function("return " + data))();
7 голосов
/ 12 июня 2010

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

7 голосов
/ 12 июня 2010

Я использую конструктор 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 итераций, может привести к сбою вашего браузера </p>

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

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

3 голосов
/ 12 июня 2010

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

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);
3 голосов
/ 12 июня 2010

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

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

2 голосов
/ 10 декабря 2011

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

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

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

...