Как работают переменные JavaScript?

Я знаю, что переменные JavaScript указывают на значение:

var foo = true;
//... later 
foo = false;

Итак, в этом примере я изменил foo, указывающий на true -> foo, указывающий на false, но если я это сделаю:

for (var i=0; i<100; i++){
    var someVar = i;
}

Я создаю новую переменную для каждой итерации?

Есть ли разница в следующих двух способах сделать то же самое?

var myvar;
for (var i=0; i<100; i++){
    myvar = i;
}

а также

for (var i=0; i<100; i++){
    var myvar = i;
}

Если да, то почему?


person pjnovas    schedule 27.10.2011    source источник


Ответы (5)


В Javascript ES5 и более ранних версиях нет области блока, только область действия. Кроме того, объявления всех переменных javascript, объявленных в области действия функции, автоматически "поднимаются" в начало функции.

Таким образом, объявление переменной в цикле ничем не отличается от объявления ее в начале функции, а затем ссылки на нее в цикле.

См. эти две ссылки для некоторых полезных пояснений: .com/2010/2/JavaScript-Scoping-and-Hoisting и http://net.tutsplus.com/tutorials/javascript-ajax/quick-tip-javascript-hoisting-explained/.

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

function a() {
    for (var i=0; i<100; i++){
        var myvar = i;
    }
}

Это работает следующим образом:

function a() {
    var myvar;
    for (var i=0; i<100; i++){
        myvar = i;
    }
}

Если вы хотите создать новую область видимости внутри цикла for, вы можете использовать IIFE (немедленно вызываемое функциональное выражение) следующим образом:

function a() {
    for (var i=0; i<100; i++){
        (function() {
            var myvar = i;
            // myvar is now a separate variable for each time through the for loop
        })();
    }
}

Обновление в 2015 году. ES6 (или иногда называемый ES2015) предлагает объявление let, которое предлагает область действия блока. В этом случае объявление переменной let поднимается только наверх текущей области блока. По состоянию на середину 2015 года это еще не широко реализовано в браузерах, но скоро появится и будет доступно в серверных средах, таких как node.js, или через транспилеры.

Итак, в ES6, если вы сделали это:

for (let i=0; i<100; i++){
    let someVar = i;
}

И i, и someVar будут локальными только для цикла.

person jfriend00    schedule 27.10.2011

Нет, нет никакой разницы; в JavaScript переменные ограничены на уровне функций, а не на уровне блоков.

person icktoofay    schedule 27.10.2011
comment
Пока этот ответ, может показаться чрезмерно простым. Это абсолютно правильно. И дело, честно говоря, в этом простом. Если вы хотите продуктивно использовать Javascript, вы должны понимать область действия функции и область действия блока, +1 - person Joe; 27.10.2011
comment
Область видимости переменной внутри цикла не меняется при последующих итерациях. Этот ответ и ваша поддержка неверны. Разница есть, хотя она тонкая и не должна влиять на выполнение любого скрипта. Я подробно описал это ниже. - person Daniel Teichman; 27.10.2011

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

Что более интересно, так это иерархическая цепочка областей действия, в которой JavaScript ищет имена, когда хочет их найти. Он просматривает цепочку областей действия от local до global, пока не найдет первый экземпляр указанного имени.

Вот почему вы можете играть в такие игры, чтобы раздражать своих друзей:

function foofinder() {
    var bar = function () { return foo; },
        foo="beers";
    return bar();
}

foofinder();
>>> "beers"
person kojiro    schedule 27.10.2011
comment
Оператора var нет. Существует объявление переменной, которое начинается с ключевого слова var. Фраза it doesn't matter where you put var as long as it occurs before one definition of myvar не имеет для меня смысла. Объявления обрабатываются до выполнения любого кода, поэтому вы можете объявлять переменные ниже, где им выполняются назначения (хотя это может привести к путанице), и их область действия по-прежнему будет контролироваться объявлением. - person RobG; 27.10.2011
comment
Несколько уважаемых ресурсов ссылаются на использование var как оператора var. Одним из таких ресурсов является JSLint (выполните поиск оператора var на странице). действительно используется для объявления переменных, вы, конечно, можете сделать определение переменной в том же выражении для удобства. Если вы определяете и переопределяете переменную в одной и той же области, вы можете поместить ключевое слово var перед любым из этих определений для того же эффекта. - person kojiro; 27.10.2011
comment
При проверке действительно существует VariableStatement (ECMA-262 § 12.2), поэтому я исправлен. Ваша формулировка была неясна (мне), моя попытка исправления усугубила ее. :-( - person RobG; 27.10.2011

Как сказал @icktoofay, в javascript нет никакой разницы. В некоторых языках на каждой итерации создается экземпляр переменной, затем она выходит за пределы области видимости и остается для сборки мусора.

Чтобы имитировать это в javascript, вы можете сделать:

for (var i=0; i<100; i++){
    (function(){
        var myvar = i;
    })();
}

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

person Ortiga    schedule 27.10.2011
comment
Ухх, это на самом деле ничего не выполняет. Ничто не вызывает внутреннюю функцию. Возможно, вы хотели создать IIFE (немедленно вызываемое функциональное выражение), но то, что у вас здесь, ничего не выполняет. - person jfriend00; 22.07.2015
comment
@jfriend00 jfriend00 Я хотел представить действие по созданию переменной в области действия функции внутри for, чтобы ее область действия не вытекала за пределы цикла. То, как будет вызываться функция, не имеет значения и не включено (хотя именованная функция тем не менее будет просачиваться). - person Ortiga; 22.07.2015
comment
Я предлагаю вам улучшить свой ответ. Это абсолютно уместно делать что-либо полезное, поскольку невызванная анонимная функция ничего не делает. На самом деле, пока она не будет вызвана, она даже не создает новую переменную. - person jfriend00; 22.07.2015

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

Практически для любого приложения нет функциональной разницы, но она все же есть, и разница может быть заметна после сотен тысяч или миллиардов циклов. Однако повторяющиеся объявления VAR с одним и тем же именем в функции приводят к фатальным исключениям в Chrome/V8.

ОБНОВЛЕНИЕ: использование var перед именем переменной доказуемо медленнее, чем отсутствие var, как показано в следующем тесте производительности в Chrome / v8 с помощью консоли JavaScript.

var startTime = new Date().getTime();
var myvar;
for (var i=0; i<100000; i++){
    myvar = i;
}
console.log(new Date().getTime() - startTime);

var startTimx = new Date().getTime();
var myvax;
for (var j=0; j<100000; j++){
var myvax = j;
}
console.log(new Date().getTime() - startTimx);
161
169

Первый тест был выполнен за 161 мс, а второй тест (с var) занял 169 мс. Это разница в 7 мс, стабильная после нескольких прогонов теста.

Весь тест был вставлен в консоль Chrome JavaScript, а затем скомпилирован перед его выполнением, поэтому первый вывод не отображается после первого вызова console.log().

Попробуй сам!

person Daniel Teichman    schedule 27.10.2011
comment
Почему за этот ответ проголосовали? Разве не очевидно, что многократное объявление переменной в цикле излишне? - person katspaugh; 27.10.2011
comment
Я сомневаюсь, что какие-либо текущие реализации JavaScript сбрасывают переменную в значение undefined каждый раз в цикле. Технически переменная объявляется только один раз в начале функции. - person Matthew Crumley; 27.10.2011
comment
@ Мэтью Крамли, ну, var x = 10; var x; подтверждает твою точку зрения. Это соответствующая часть: code.google. com/p/v8/source/browse/trunk/src/ast.cc#90? - person katspaugh; 27.10.2011
comment
Дэниел. Я сделал этот тест ниже из комментария @katspaugh, и переменная x больше не инициализируется, она все еще имеет значение 10. - person pjnovas; 27.10.2011
comment
Я прочитал спецификацию, которая раньше не приходила мне в голову, и в ней говорится: если varAlreadyDeclared ложно, то (Ecma-262, 10.5), по сути, объявить его и установить на undefined, а нет else-пункт. - person katspaugh; 27.10.2011
comment
Запустите его сами. jsperf может быть неточным. - person Daniel Teichman; 29.10.2011
comment
@Daniel: я запустил его с node.js после изменения количества итераций на 1 000 000 000, и между ними постоянно очень небольшая разница (~ 0-2 мс). - person icktoofay; 29.10.2011