Змінні JavaScript оголошують зовнішній або внутрішній цикл?


213

В AS3 я вважаю, що ви повинні ініціалізувати всі змінні зовнішні цикли для підвищення продуктивності. Це так і з JavaScript? Що краще / швидше / найкраща практика?

var value = 0;

for (var i = 0; i < 100; i++)
{
    value = somearray[i];
}

або

for (var i = 0 ; i < 100; i++)
{
    var value = somearray[i];
}

7
Зовні! завжди назовні.
BGerrissen

37
Хм, чи не зміщуються декларації змінних, щоб все-таки функціонувати як в Javascript, так і в AS3? Якщо я маю рацію, то це насправді не має значення.
витрачається

3
@Andy - Ви намагалися призначити, перш ніж оголосити функціональне тіло? Можливо, ваші застереження обманюють вас. Працездатність WRT, при натисканні на масштабування, якщо JS інтерпретується, то він буде пережовувати зайві цикли в блоці циклу. Якщо буде складено (що більшість двигунів робить сьогодні), це не має значення.
витрачається

2
Чудове запитання! Дякую. Прочитавши всі відповіді, я вважаю, що якщо це лише невеликий цикл або лише тимчасова змінна, я триматиму їх там, де вони потрібні, і це не вплине на продуктивність. Якщо вар використовується функцією не один раз, чому б не посилатися на неї всередині функції та, нарешті, на глобальних точках, тоді можна сісти за межами fn ()
Дін Механ,

3
Я здивований, що ніхто не намагався виміряти продуктивність. Я створив jsperf . Здається, трохи швидше, коли оголошується всередині циклу для Safari та Firefox, навпаки для Chrome…
Buzut

Відповіді:


281

Немає різниці в значенні та продуктивності в JavaScript або ActionScript.

var- це директива для аналізатора, а не команда, що виконується під час виконання. Якщо певний ідентифікатор був оголошений varраз або більше в будь-якому місці функціонального тіла (*), то все використання цього ідентифікатора в блоці буде посилатися на локальну змінну. Не має значення, чи valueоголошено йогоvar всередині циклу, поза циклом, або обидва.

Отже, ви повинні написати те, що вам найбільше читається. Я не погоджуюсь з Крокфордом, що ставити всі вищі вершини функції - це завжди найкраще. У випадку, коли змінна використовується тимчасово в розділі коду, краще оголосити varв цьому розділі, тому розділ стоїть окремо і його можна скопіювати. В іншому випадку скопіюйте декілька рядків коду в нову функцію під час рефакторингу, не вибираючи окремо та переміщуючи пов’язане var, і ви отримали випадковий глобальний результат.

Зокрема:

for (var i; i<100; i++)
    do something;

for (var i; i<100; i++)
    do something else;

Крокфорд порекомендує вам видалити друге var(або видалити обидва vars і зробити var i;вище), і jslint буде збиватися з вас для цього. Але в IMO важче зберегти обидва vars, зберігаючи весь пов'язаний код разом, замість того, щоб мати додатковий, легко забутий біт коду у верхній частині функції.

Особисто я схильний заявляти як varперше призначення змінної у незалежному розділі коду, чи є інше окреме використання того ж імені змінної в якійсь іншій частині тієї ж функції. Для мене varвзагалі декларувати - це небажана бородавка JS (краще було б мати змінні за замовчуванням для локальних); Я не вважаю своїм обов'язком дублювати обмеження [старої редакції] ANSI C також у JavaScript.

(*: крім вкладених функціональних органів)


4
Я досі не можу визначитися, чи є я з Крокфордом чи ні на цьому. Оголошення змінних всередині блоків трохи схоже на використання висловлювань умовної функції (що неслухняно) ... Однак я згоден і з вашими пунктами :) #confused
Даніель Вассалло

20
+1 Я не погоджуюся з міркуваннями Крокфорда (цитується у відповіді Даніеля), оскільки розробникам JavaScript ми не повинні писати код, щоб інші програмісти "сімейства C" могли це зрозуміти. Я часто використовую УАГ всередині , якщо блоки і петлі , тому що це має сенс для мене.
Енді Е

4
-1 ОП запитує, чи слід оголошувати зміни в тілі петлі до початку циклу. Значення циклу циклу, очевидно, є особливим випадком (і піднімається) і зовсім не допомагає ОП.
mkoistinen

21
Індекси циклу - це не окремий випадок, вони обробляються та піднімаються точно так само, як і звичайне завдання.
bobince

31
+1 Крокфорд помиляється з цього приводу (та інших, але я відхиляюся). Вимагати varвикористання лише у верхній частині функції просто вимагає створення випадкової глобальної змінної. І мати масу непов'язаних змінних, які всі оголошуються в одному місці, є семантично безглуздим, особливо коли деякі з цих змінних можуть бути використані ніколи.
MooGoo

64

Теоретично це не повинно змінювати JavaScript, оскільки мова не має блочного кола, а лише область застосування.

Я не впевнений у аргументі продуктивності, але Дуглас Крокфорд все-таки рекомендує, щоб varвисловлювання були першими твердженнями в функціональному органі. Цитування з конвенцій коду для мови програмування JavaScript :

У JavaScript немає області блоку, тому визначення змінних у блоках може переплутати програмістів, які мають досвід роботи з іншими мовами сімейства C. Визначте всі змінні у верхній частині функції.

Я думаю, що у нього є пункт, як ви бачите в наступному прикладі. Оголошення змінних у верхній частині функції не повинно заплутати читачів у думці, що змінна i міститься в області forблоку циклу:

function myFunction() {
  var i;    // the scope of the variables is very clear

  for (i = 0; i < 10; i++) {
    // ...
  }
}

8
+1 для розповідання істин ОП про сферу JS. Мені цікаво, чи можна спростувати відповіді, які говорять інакше!
витрачається

1
@Kieranmaine: Я не сказав, що це не впливає на продуктивність. Я щойно зробив аргумент за те, щоб розмістити їх поза циклами, що не стосується продуктивності .... Я не маю жодних посилань на аргументи щодо ефективності, але ви також не вказали жодної у своїй відповіді :)
Даніель Вассалло,

1
@Kieranmaine: у вас є джерело для цього?
Енді Е

5
@Kieranmaine: AFAIK, навіть якщо ви оголосите змінні всередині циклу, ecma- / javascriptпідніме їх під час виконання. Це називається "Підйомник". Тому різниці не повинно бути.
jAndy

1
Як впливає ES6 letна цю відповідь?
jbyrd

58

ECMA-/JavascriptМова hoistsбудь-якої змінної , яка оголошена в будь-якому місці у верхній частині функції. Це відбувається тому , що ця мова дійсно є function scopeі не НЕ мають , block scopeяк і багато інших C-подібних мов.
Це також відоме як lexical scope.

Якщо ви заявляєте щось подібне

var foo = function(){
    for(var i = 0; i < 10; i++){
    }
};

Це дістається hoistedдо:

var foo = function(){
    var i;
    for(i = 0; i < 10; i++){
    }
}

Тож це не має ніякої різниці у продуктивності (Але виправте мене, якщо я тут абсолютно помиляюся).
Набагато краще аргумент НЕ оголошувати змінну де - небудь ще , ніж у верхній частині функції читаність . Оголошення змінної всередині for-loopможе призвести до помилкового припущення, що доступ до цієї змінної можна отримати лише в тілі циклу, що абсолютно неправильно . Infact ви можете отримати доступ до цієї змінної в будь-якому місці в межах поточного діапазону.


Основна відповідь, що і прийнята, але, IMO, читабельніша і настільки ж інформативна. Хороша робота.
Енн Ганн

5
Як впливає ES6 letна цю відповідь?
jbyrd

13

У наступному році всі браузери матимуть двигуни JS, які попередньо компілюють код, тому різниця в продуктивності (яка виникає від розбору того ж блоку коду знову і знову плюс виконання завдання) повинна стати незначною.

Крім того, ніколи не оптимізуйте продуктивність, якщо вам цього не доведеться. Утримуючи змінні поблизу місця, де вони вам потрібні, перший раз зберігає ваш код чистим. З негативної сторони люди, які звикли до мов із областями блоку, можуть заплутатися.


6

Інше враження, що тепер у нас letі constв ES2015, полягає в тому, що тепер ви можете застосовувати змінні спеціально до блоку циклу. Тому, якщо вам не знадобиться однакова змінна поза циклом (або якщо кожна ітерація залежить від операції, виконаної з цією змінною в попередній ітерації), мабуть, бажано це зробити:

for (let i = 0; i < 100; i++) {
    let value = somearray[i];
    //do something with `value`
}

4

Я щойно робив простий тест у Chrome. Спробуйте загадку у вашому браузері і подивіться результати

  var count = 100000000;
    var a = 0;
    console.log(new Date());

    for (var i=0; i<count; i++) {
      a = a + 1
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
      a = a + 1;
    }

    console.log(new Date());

    var j;
    for (j=0; j<count; j++) {
        var x;
        x = x + 1;
    }

    console.log(new Date());

Результатом є те, що останній тест займає ~ 8 секунд, а попередній - лише ~ 2 секунди. Дуже повторно та незалежно від порядку.

Отже, це доводить мені, що завжди слід оголошувати вари за межами циклу. Цікавий для мене випадок перший, де я заявляю iу заяві for (). Цей, здається, такий же швидкий, як і 2-й тест, де я попередньо оголосив індекс.


14
@KP: результати підтверджуються лише в тому випадку, якщо ви перевірите їх самостійно або якщо велика кількість людей перевіряє їх. @mkoistinen: Я створив більш справедливий тест, jsfiddle.net/GM8nk . Після запуску сценарію кілька разів у Chrome 5, я помітив, що не було послідовного переможця. Усі три варіанти вийшли краще, ніж інші, після декількох оновлень. -1 від мене, боюся. Зауважте, ви можете запустити цю програму в інших браузерах. IE і Fx не любили 100 мільйонів ітерацій.
Енді Е

1
@AndyE. Нічого собі, так що, виходячи з цього простого тесту, IE смокче на 100 разів більше? =)
mkoistinen

2
Результати для мене всюди є, явного переможця веб-браузера немає, хоча іноді значні різниці в швидкості. Дивно. Я думаю, що загадка Енді є кращим тестом, якщо кожен кандидат має власну функцію ... звичайно, якщо оригінальний сценарій працює поза функцією, він насправді не повинен нічого тестувати, оскільки varзаявляє про глобальну змінну, яка б все одно бути глобальним.
bobince

4
Через рік після факту, але SHAPOW
sdleihssirhc

2
Це не моє, але я вважав, що хтось із вас буде зацікавлений: jsperf.com/var-in-for-loop
m1.

1

JavaScript - це мова, написана внизу на C або C ++, я не дуже впевнений, яка це мова. І однією з його цілей є збереження запасу внутрішньої пам'яті. Навіть у C або C ++ вам не доведеться турбуватися про те, чи буде вона споживати багато ресурсів, коли змінні оголошуються всередині циклу. Чому варто турбуватися про це в JavaScript?


1
C або C ++ може бути внизу JavaScript. Але ми не повинні забувати, що браузер перетворює JavaScript на основну мову (C, C ++). Отже, ефективність залежить від браузера
Кіра,

3
@Kira Це насправді не перетворюється на C / C ++. Javascript збирається в набір інструкцій, які виконуються під час виконання JS (тобто віртуальна машина, що працює в браузері). Цей же принцип застосовується і до інших динамічних мов, таких як Python та Ruby.
Ентоні Е

@AnthonyE, Дякую за інформацію Я не був впевнений у перетворенні JS на C або C ++. Тому я звик бути у своєму коментарі
Кіра,

0

Ну, це залежить від того, що ви намагаєтеся досягти ... якщо valueприпустимо, що це лише тимчасова змінна всередині блоку циклу, то використовувати другу форму набагато зрозуміліше. Це також більш логічно і багатослівно.


Я виявив, що натискання всіх оголошень змінної на початок - включаючи тимчасові змінні - насправді може призвести до плутанини, оскільки воно просто стає "галасливим".
Даніель Соколовський

0

Це не має значення, якщо ви оголошуєте змінні всередині або зовні для циклу. Нижче наведено зразок коду для тестування.

function a() {
   console.log('Function a() starts');
   console.log(new Date());
    var j;
    for (j=0; j<100000000; j++) {
        var x;
        x = x + 1;
    }
    console.log(new Date());
    console.log('Function a() Ends');
}
a()
function b() {
console.log('Function B() starts');
   console.log(new Date());
    var a;
    var j;
    for (j=0; j<100000000; j++) {
      a = a + 1;
    }
    console.log(new Date());
    console.log('Function B() Ends');
}
b()

Результати показали в моєму випадку

Function a() starts
VM121:3 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:9 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:10 Function a() Ends
VM121:14 Function B() starts
VM121:15 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:21 Thu Apr 12 2018 15:20:26 GMT+0530 (India Standard Time)
VM121:22 Function B() Ends

Дякую - MyFavs.in


в обох випадках ви оголошуєте j поза циклом! X_x
John ktejik

Я спробував це в Chromium 81 з letзамість цього varі, a()як правило, трохи повільніше (як 120 проти 115 мс = ~ 6% = ІМО незначний)
mikiqex

-1

Питання в основному полягає в тому, щоб оголосити var всередині циклу. Подумайте, що станеться, якщо ви це зробите:

var a = 30;
var a = 50;
var a = 60;

Ви вважаєте, що це правильно? Ні ... тому що ви не хочете оголошувати змінну так багато разів. Коли ви оголошуєте змінну всередині циклу, чи не оголошується вона стільки разів, коли цикл працює? Очевидно, це ляпне вам, коли ви перебуваєте в режимі "суворого використання". Люди не погодилися з Крокфордом, не замислюючись над початковим питанням.

Тому завжди добре оголошувати змінні вгорі - 1. Для читабельності, 2. Створення хороших звичок.


1
"Коли ви оголошуєте змінну всередині циклу, чи не вона оголошує стільки разів, скільки цикл працює?" <- Ні, це неправильно. Оголошення змінної стає піднятим, тому все, що вам залишається, - це призначення.
Маттіас

-2

Що стосується продуктивності після запуску тесту на Chrome, Firefox та jsperf в ОС Linux, то, схоже, є різниця в продуктивності між декларацією змінних у циклі та поза циклом. Це невелика різниця, але це також ускладнюється кількістю ітерацій та кількістю змінних оголошень.

Тому для кращої продуктивності я б запропонував оголосити змінні поза циклом. Або ще краще оголосити свої змінні в рядку. Див. Приклад.

// inline
for (var ai = 0, al = 100000000, av; ai < al; ai++) {
    av = av + 1;
}

// outside
var bv;
var bl = 100000000;
for (var bi = 0; bi < bl; bi++) {
    bv = bv + 1;
}

Зверніть увагу, як змінна 'al' і 'av' знаходяться в рядку декларування циклу. Ця вбудована декларація забезпечила мені стабільно кращі показники. Навіть над оголошенням змінних за межами циклу. Знову різниця у виконанні дійсно невелика.

https://jsperf.com/outside-inline-for-loop-ase/1


Для мене ваш тест дав всередині циклу. І як це не сталося, різниця є занадто малою для висновку, і прийнята відповідь чітко пояснює, що різниці немає
Ulysse BN

Оскільки декларації змінних піднімаються, різниці насправді немає.
trincot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.