Есть ли снижение производительности при замене локальных переменных аргументами в Javascript?

Есть ли снижение производительности при написании функции, в которой локальные операторы var заменяются аргументами? Пример:

function howManyMatch(arr, pattern, /*ignored:*/ i, l, total) {
  l = arr.length;
  total = 0;
  for (i = 0, i < l; i++) {
    if (pattern.test(arr[i]))
      total++;
  return total;
}

Некоторые преимущества:

  • меньший минимизированный размер: нет операторов var;
  • меньше времени программиста тратится на попытки использовать как можно меньше var
  • все локальные переменные определены в одном месте

...и недостатки:

  • arguments можно изменить неожиданным образом. Смотри ниже
  • менее ясно в теле, что вары являются локальными
  • смущает видеть аргументы, которые ничего не делают
  • если кто-то по незнанию удалит их, ваш код запишется в глобалы

Тем не менее, это может быть простой способ для минификатора автоматически выжимать больше битов.

Обновление: пока не упомянут большой недостаток: если функция вызывается с N параметрами, первые N элементов в arguments будут привязаны к первым N идентификаторам в списке аргументов (см. последний пункт 10.1.8). Учти это:

function processStuff(/*ignored:*/i, j, k) {
    // use i/j/k to loop
    // do stuff with the arguments pseudo-array
}

В приведенном выше примере, если вы вызвали processStuff(stuff1, stuff2), установка i и j перезаписала бы arguments[0] и arguments[1] соответственно.


person Steve Clay    schedule 06.12.2010    source источник
comment
Мой вопрос был в основном о влиянии на производительность. Я, вероятно, не буду использовать эту практику. Некоторые люди, похоже, не понимают, что объединение всех операторов var в один снижает читабельность: вы в основном злоупотребляете оператором запятой, чтобы втиснуть несколько операторов вместе. Видя, что компилятор Closure может сделать эту оптимизацию за вас, более читабельно просто иметь одно объявление/назначение var на строку.   -  person Steve Clay    schedule 09.12.2010


Ответы (5)


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

Теперь, отвечая на вопрос, я не думаю, что это повлияет на производительность.

Позвольте мне немного рассказать о процессе создания экземпляра переменной, который занимает место для кода функции, непосредственно перед выполнением функции (обычно называемое «подъемом»), сначала все формальные параметры, описанные для функции, привязываются к текущему объекту переменной (текущей области действия) , и они инициализируются значениями, переданными при вызове функции, или undefined, если они не указаны.

После этого все идентификаторы, принадлежащие всем операторам var внутри функции, объявляются в текущей области видимости и инициализируются с помощью undefined (обратите внимание, что назначения выполняются после этого, тело функции фактически еще не выполняется).

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

(function (a) {
  return typeof a; // "function", not "string"

  function a () {}

})('foo');  // <-- passing a string

Вместо этого я бы рекомендовал просто использовать один оператор var в верхней части функции:

function howManyMatch(arr, pattern) {
  var l = arr.length,
      total = 0, i;
  for (i = 0, i < l; i++) {
    if pattern.test(arr[i]) && total++;
  return total;
}

Это не просто упорядочивает ваш код, это поможет вам предотвратить нежелательные результаты из-за того, что область JavaScript состоит только из функций и «подъемной» природы var, некоторых инструментов, таких как JSLint поощряет и это.

person Christian C. Salvadó    schedule 06.12.2010
comment
Спасибо за ссылки на спецификации. Я обновил вопрос, чтобы добавить еще один недостаток, связанный с тем, как формальные параметры привязаны к arguments. Я, вероятно, не каждый буду использовать этот трюк. - person Steve Clay; 08.12.2010

Нет, не делай этого. Это сбивает с толку и не нужно. И я нахожу ваш список «преимуществ» довольно благовидным — каждый пункт в нем очень скудный в отношении фактической полученной выгоды.

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

function howManyMatch(arr, pattern) {
  var i, l, total;
  // rest
}

Или вы также можете объявить/определить все за один шаг.

function howManyMatch(arr, pattern) {
  var l = arr.length, total = 0, i = 0;
  // rest
}
person Peter Bailey    schedule 06.12.2010

Я думаю, что читабельность и ремонтопригодность перевешивают размер файла и микрооптимизацию. Код с ключевым словом var читать гораздо легче. Кроме того, должно быть достаточно одного оператора var на область видимости (во всяком случае, именно там JavaScript их поднимает). Все локальные переменные доступны везде в локальной области видимости (независимо от порядка их объявления). Таким образом, все локальные переменные должны быть объявлены в одной и той же позиции (в начале локальной области видимости) для лучшей читабельности. Эти четыре байта для оператора var действительно не стоят того, чтобы вводить возможные ошибки, позволяя пользователю устанавливать начальные значения ваших локальных переменных, вызывая эту функцию с дополнительными параметрами. Это нарушает инкапсуляцию (вы можете сделать это правильно, но в итоге вы получите больше байтов, чем сэкономили, опустив var). Кроме того, это действительно сбивает с толку любого, кто пытается прочитать ваш код.

person jwueller    schedule 06.12.2010

Те же преимущества, которые вы даете, могут быть достигнуты путем простого удаления ключевого слова var:

function howManyMatch(arr, pattern) {
  l = arr.length;
  total = 0;
  for (i = 0; i < l; i++) {
    if pattern.test(arr[i]) && total++;
  return total;
}

Нет необходимости явно писать ключевое слово var, так как в этом случае вы определяете все переменные со значением ( l = arr.length, total = 0, i = 0 ).

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

function howManyMatch(arr, pattern, i=0, l, total = 0){ ... }

Итак, я не думаю, что ваше решение по минимизации кода в конце концов очень полезно, так как недостатки остаются;)


Изменить

Я не думал о том, что определение переменных без ключевого слова var превратит их в глобальные. Это может быть то, чего вы совсем не хотите...

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

function howManyMatch(arr, pattern) {
  var l = arr.length, total = 0, i=0;
  for (; i < l; i++) {
    if pattern.test(arr[i]) && total++;
  return total;
}

А этот пример еще короче.

person Harmen    schedule 06.12.2010
comment
Отсутствие оператора var делает все используемые переменные (не являющиеся аргументами) неявными глобальными, что очень плохо. Вы не должны этого делать. l, i и total будут глобальными переменными после выполнения этой функции. - person jwueller; 07.12.2010
comment
@elusive, я не думал об этом, ты прав. После вызова функций переменные действительно будут глобальными. - person Harmen; 07.12.2010

Кристиан Йохансен в «Test-Driven Javascript Development» (2011) утверждает: «В общем, объект arguments следует использовать только тогда, когда формальные параметры не могут решить проблему, потому что его использование приводит к снижению производительности. Фактически, просто ссылка объект вызовет некоторые накладные расходы, указывая на то, что браузеры оптимизируют функции, которые его не используют».

person Charles Short    schedule 14.06.2012