2014-01-13 4 views
3

Все началось с этих простых строк кода:Переменная Scope и Var

a = 3; 
b = 2; 

function line(x) { 
    var a = 5; 
    var b = 4; 

    return a*x+b; 
} 

// returns 17 
b = line(a) - b; 
alert(b); 

// returns 36 
c = line(a) + b; 
alert(c); 

Оба предупреждения возврата 17 и 36, соответственно.
Управление работает должным образом. До ...


Я не сделать ошибку изменения
внутри функции следующим образом:

function line(x) { 
    var a = 5; 
     b = 4; 

    return a*x+b; 
} 

Внезапно линия 13 возвращается 15, строка 17 возвращает 23
и ситуация продолжает ухудшаться Я следую за var
вниз по кроличьей дыре, становясь более запутанной, когда я делаю свой спуск.

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

Вот ссылки на четырех адов,
сделанные (и, возможно,) мне:

Риддл # 1 http://jsfiddle.net/js_test/gNEmY/

Загадка # 2 http://jsfiddle.net/js_test/FJVYL/

Риддл # 3 http://jsfiddle.net/js_test/Vz7Sd/

Риддл # 4 http://jsfiddle.net/js_test/RaA5J/

Если кто-то может дать мне понять
в том, что происходит под капотом
именно, что происходит во время каждого предупреждения() вызов;
Я был бы очень признателен.

+0

возможно дубликат [Разница между использованием вар и не используя вар в JavaScript] (http://stackoverflow.com/questions/1470488/difference-between-using-var-and-not -using-var-in-javascript) – Bergi

+0

Спасибо @Bergi Я так много понимаю. Я больше ищут, что происходит в одной из загадок. – Wilhelm

+0

Varaible [Активация связывания с буквами] (http://ecma-international.org/ecma-262/5.1/#sec-10.5) описана в ECMA-262. * VariableEnvironment * можно рассматривать как недоступный объект, свойства которого являются локальными переменными. – RobG

ответ

2

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

Существует также новое ключевое слово let, которое входит в будущую версию ECMAScript и уже находится в некоторых браузерах, что создает переменную с блочной областью.

Итак, в вашем первом примере (предположим, что это работает в браузере) вы создаете window.a и window.b, которые привязаны к 3 и 2 соответственно.Функция line объявляет локальные переменные a и b. Когда window.a передается в строку, параметр x привязан к значению 3 (значение window.a). Локальные переменные a и b составляют 5 и 4 соответственно. Они не имеют ничего общего с window.a и window.b. Расчет a*x+b, таким образом, 5 * 3 + 4, или 19.

Код b = line(a) - b проходит window.a к line, вычисляет значение 19, вычитает текущее значение window.b из него (2), в результате чего в 17, который затем присваивается window.b. Значение 36 исходит от line(3) (это все еще 19) плюс 17 (новое значение window.b).

Когда вы удалили var от присвоения b в функции line, вы не изменили его так, чтобы b больше не является локальной переменной. В этом случае все ссылки на b в функции относятся к глобальному значению window.b.

В случае вашего Загадки №2, где у вас 23, после первого вызова window.b равно 15. Когда строка (a) вызывается второй раз, window.b устанавливается на 4 , вычисление a * x + b по-прежнему получает 19, а затем снова добавляется окно.b (4), что делает 23.

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

function scope(array) { 
    var a = 7; 

    for (var b = 0; b < array.length; ++b) { 
     var c = array[b]; 
    } 

    alert(a + b + c); 
} 

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

Переменная, которая не объявлена ​​в определенной области, необязательно ссылается на глобальную переменную. Если он вложен в область другой функции, он будет ссылаться на эту вложенную область. Рассмотрим следующий пример:

var b = 7; 

function outer() { 
    var b = 42; 

    function inner() { 
     return b; // refers to b in outer, not global 
    } 

    return inner(); 
} 

alert(outer()); // 42, not 7 
+0

Это лучший способ выразить это. Я имел в виду, что b не объявляется в 'inner', а не что он вообще не объявлен. –

+0

Большое спасибо @DavidConrad – Wilhelm

0

Переменная, объявленная без var, является глобальной переменной, это правило. Но помните, если вы хотите объявить несколько локальных переменных одним var, разделите их запятой.

var a = 5; // local variable 
    b = 4; // global varialbe 

var a = 5, // local variable 
    b = 4; // local varialbe 
+0

«Переменная, объявленная без« var », не является секвенсором. Переменная может ** быть объявлена ​​** только с помощью 'var' (хотя включение в формальные параметры объявления функции - это объявление переменной). – RobG

0

Если вы находитесь в глобальном масштабе, то нет никакой разницы.

Если вы находитесь в функции, то «var» создаст локальную переменную, «no var» будет искать цепочку областей действия до тех пор, пока не найдет переменную или не попадет в глобальную область (в какой момент она ее создаст) ,

Source

В случае вашей первой функции, line(a) обрабатывается первым, и к тому времени она работает, вы установили b = 4.Таким образом, и потому b это глобальная переменная, когда она работает line(a) - b, вы получаете:

line(a) (возвращает 19)

и минус b (Ь = 4)

19 - 4 = 15

Вы можете упростить заявление и получить тот же результат:

//define global variable b 
var b = 2; 
function line(x) { 
    //x = 3 
    //update global variable b; it now = 4 
    b = 4; 
    //forget the math and just return 19 
    return 19; 
} 

//run function; before, b = 2, after b = 4 
b = line(3) - b; 
alert(b); 

Аналогично вашему второму примеру, поскольку ваша функция меняет значение глобальной переменной b, единственное различие заключается в том, что вы - , добавив 4 вместо вычитания (19 + 4 = 23).

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

var globalVar; 
function someFunc(x) { 
    var tempVar = 4; 
    return x * tempVar; 
} 
+0

Спасибо @ r3mus Я так много понимаю. Не могли бы вы проверить [Riddle # 2] (http://jsfiddle.net/js_test/FJVYL/) Я все еще смущен, что происходит, когда я вызываю «alert (a);' after 'c = line (a) + b; ' – Wilhelm

+0

Опять же, вы обновили' a', так что теперь он = 4 (вы сделали это, когда вы впервые вызывали 'line (a)'. Когда вы запускаете его второй раз, значение 'a' (= 5). – brandonscript

+0

Я понимаю, как 'local' может обновлять переменную' global', когда 'var' опущен. Однако в отношении [Riddle # 2] (http://jsfiddle.net/js_test/ FJVYL /), я смущен, как 'c = line (a) + b' может' return' 23, если 'b' становится' b = строка (a) - b' перед вторым вызовом 'alert'? Почему 'c = line (a) + b' использовать' local' дважды (например, 5 * 3 + 4 + 4) вместо локальной переменной и обновлять 'global' (например, 5 * 3 + 4 + b = строка (a) - b; ')? – Wilhelm

0

VAR объявляет новый экземпляр в памяти для новой переменной. Просто скажите b = 4 попытается изменить значение b на 4. Если b не был инициализирован, ничего не произойдет.

Это связано с понятием PRIVATE и LOCAL переменных и соглашений об именах.

Во-первых, у вас есть две переменные, называемые a, а 2 - b. Это плохая практика, потому что вы можете ошибочно изменить значение другого, или его старое значение все еще может находиться в памяти, потому что оно уже было инициализировано и дано значение. Помните, что вам не всегда нужно назначать значение при инициализации, это просто лучшая практика.

Во-вторых, ваша функция может изменять переменную от уровня выше нее. Другими словами, убрав VAR на эту одну строку, можно изменить исходное значение b, которое вы установили в 2 в начале файла.

TLDR; Не используйте одно и то же имя переменной более одного раза и всегда используйте свой VAR при попытке создать новую переменную.

+1

Это не «в зависимости от правил вашего компилятора», а правила Javascript. Если у вас есть вложенные функции, а внешняя функция объявляет переменную, а внутренняя функция ссылается на нее, не объявляя ее, она * будет * обращаться к ней «с уровня выше». –

+1

Моя ошибка. Я исправил это. Спасибо. – Alexander

Смежные вопросы