Разница между синтаксисами объявления переменных в Javascript (включая глобальные переменные)?

292

Есть ли разница между объявлением переменной:

var a=0; //1

...сюда:

a=0; //2

...или:

window.a=0; //3

в глобальном масштабе?

Дэн
источник
2
AFAIK var a = 0; не работает в IE при доступе к переменной через другой внешний js-файл, который объявлен в другом js-файле
Aivan Monceller
Я не знаю о window.a, но другие 2 способа одинаковы в глобальном масштабе.
программист
1
@AivanMonceller действительно? ссылку пожалуйста.
Raynos
@Raynos, я испытываю это на своем собственном сайте. IE6, чтобы быть конкретным. Мне не удалось отобразить мое var enum, которое находится во внешнем js-файле, и я ссылаюсь на него как на встроенный javascript в html-файле
Aivan Monceller
@Ashwini В глобальной области видимости - это глобальный объект (в браузерах). var a = 1; console.log (а); console.log (win
leebriggs

Ответы:

557

Да, есть пара отличий, хотя на практике они обычно невелики.

Есть четвертый путь, а с ES2015 (ES6) есть еще два. В конце я добавил четвертый способ, но вставил пути ES2015 после # 1 (вы поймете почему), поэтому мы имеем:

var a = 0;     // 1
let a = 0;     // 1.1 (new with ES2015)
const a = 0;   // 1.2 (new with ES2015)
a = 0;         // 2
window.a = 0;  // 3
this.a = 0;    // 4

Эти заявления объяснили

# 1 var a = 0;

Это создает глобальную переменную, которая также является свойством глобального объекта , к которому мы получаем доступ как windowв браузерах (или через thisглобальную область видимости в нестрогом коде). В отличие от некоторых других свойств, свойство не может быть удалено с помощью delete.

В терминах спецификации он создает привязку идентификатора для объекта Environment Record для глобальной среды . Это делает его свойством глобального объекта, поскольку в глобальном объекте хранятся привязки идентификаторов для объекта среды. Запись среды. Вот почему свойство не удаляется: это не просто свойство, это привязка идентификатора.

Привязка (переменная) определяется до запуска первой строки кода (см. «Когда это varпроисходит» ниже).

Обратите внимание, что в IE8 и более ранних версиях свойство on windowне перечисляется (не отображается в for..inвыражениях). В IE9, Chrome, Firefox и Opera его можно перечислить.


# 1,1 let a = 0;

Это создает глобальную переменную, которая не является свойством глобального объекта. Это новая вещь с ES2015.

В терминах спецификации он создает привязку идентификатора в декларативной записи среды для глобальной среды, а не для объекта записи среды. Глобальная среда уникальна в том , раскол по окружающей среде записи, один для всех старых вещей , которая идет на глобальный объект ( объект Environment Record) и другой для всех новых вещей ( let, constи функции , созданные class) , которые не делают перейти на глобальный объект.

Привязка создается до того, как будет выполнен любой пошаговый код в его включающем блоке (в данном случае, до запуска любого глобального кода), но он никоим образом не будет доступен , пока пошаговое выполнение не достигнет letоператора. Как только выполнение достигает letоператора, переменная становится доступной. (См. «Когда letи constпроизойдет» ниже.)


# 1,2 const a = 0;

Создает глобальную константу, которая не является свойством глобального объекта.

constточно так же, letза исключением того, что вы должны предоставить инициализатор ( = valueдеталь), и вы не можете изменить значение константы после ее создания. Под крышками это точно так же, letно с привязкой к идентификатору с флагом, указывающим, что его значение нельзя изменить. Использование constделает три вещи для вас:

  1. Делает это ошибкой времени разбора, если вы пытаетесь присвоить константу.
  2. Документирует его неизменный характер для других программистов.
  3. Позволяет оптимизировать движок JavaScript, исходя из того, что он не изменится.

# 2 a = 0;

Это неявно создает свойство для глобального объекта . Поскольку это нормальное свойство, вы можете удалить его. Я бы порекомендовал не делать этого, это может быть непонятно любому, кто читает ваш код позже. Если вы используете строгий режим ES5, выполнение этого (присвоение несуществующей переменной) является ошибкой. Это одна из нескольких причин использовать строгий режим.

И что интересно, опять же в IE8 и более ранних версиях свойство создано не перечисляемым (не отображается в for..inвыражениях). Это странно, особенно учитывая № 3 ниже.


# 3 window.a = 0;

Это явно создает свойство для глобального объекта, используя глобальный объект, windowкоторый ссылается на глобальный объект (в браузерах; некоторые среды без браузера имеют эквивалентную глобальную переменную, например, в globalNodeJS). Поскольку это нормальное свойство, вы можете удалить его.

Это свойство является перечислимо, на IE8 и более ранние версии, а также на любом другом браузере я пробовал.


# 4 this.a = 0;

Точно так же, как # 3, за исключением того, что мы ссылаемся на глобальный объект через thisвместо глобального window. Это не будет работать в строгом режиме, потому что в строгом режиме глобальный код thisне имеет ссылки на глобальный объект ( undefinedвместо него есть значение ).


Удаление свойств

Что я имею в виду под «удалением» или «удалением» a? Именно это: Удаление свойства (полностью) с помощью deleteключевого слова:

window.a = 0;
display("'a' in window? " + ('a' in window)); // displays "true"
delete window.a;
display("'a' in window? " + ('a' in window)); // displays "false"

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

Предупреждение : IE8 снова (и, по-видимому, ранее, и IE9-IE11 в сломанном режиме «совместимости»): он не позволит вам удалить свойства windowобъекта, даже если вам это разрешено. Хуже того, он выдает исключение при попытке ( попробуйте этот эксперимент в IE8 и в других браузерах). Поэтому при удалении с windowобъекта вы должны быть оборонительными:

try {
    delete window.prop;
}
catch (e) {
    window.prop = undefined;
}

Он пытается удалить свойство, и если выбрасывается исключение, он делает следующее лучшее и устанавливает для свойства значение undefined.

Это относится только к windowобъекту и только (насколько я знаю) к IE8 и более ранним версиям (или IE9-IE11 в сломанном режиме «совместимости»). Другие браузеры могут удалять windowсвойства в соответствии с приведенными выше правилами.


Когда varпроисходит

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

Это может сбивать с толку, поэтому давайте посмотрим:

display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "undefined"
display("bar in window? " + ('bar' in window)); // displays "false"
display("window.bar = " + window.bar);          // displays "undefined"
var foo = "f";
bar = "b";
display("foo in window? " + ('foo' in window)); // displays "true"
display("window.foo = " + window.foo);          // displays "f"
display("bar in window? " + ('bar' in window)); // displays "true"
display("window.bar = " + window.bar);          // displays "b"

Живой пример:

Как видите, символ fooопределяется перед первой строкой, а символ bar- нет. Где это var foo = "f";утверждение, на самом деле есть две вещи: определение символа, которое происходит до запуска первой строки кода; и выполнение присвоения этому символу, что происходит, когда строка находится в пошаговом потоке. Это называется « varподъемом», поскольку var fooдеталь перемещается («поднимается») в верхнюю часть области, но foo = "f"деталь остается в своем первоначальном положении. (См. Плохо неправильно понятvar в моем анемичном небольшом блоге.)


Когда letи constпроизойдет

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

Итак, пока это работает:

display(a);    // undefined
var a = 0;
display(a);    // 0

Это выдает ошибку:

display(a);    // ReferenceError: a is not defined
let a = 0;
display(a);

Остальные два способа , которыми letи constотличаются от var, которые не являются на самом деле отношение к вопросу, являются:

  1. varвсегда применяется ко всему контексту выполнения (по всему глобальному коду или по всему коду функции в функции, где он появляется), но letи constприменяется только внутри блока, в котором они появляются. То есть varимеет функцию (или глобальную) область видимости, но letи constимеет область видимости блока.

  2. Повторение var aв том же контексте безвредно, но если у вас есть let a(или const a), наличие другого let aили a const aили a var aявляется синтаксической ошибкой.

Вот пример, демонстрирующий это letи constвступающий в силу немедленно в их блоке до запуска любого кода в этом блоке, но недоступный до оператора letor const:

var a = 0;
console.log(a);
if (true)
{
  console.log(a); // ReferenceError: a is not defined
  let a = 1;
  console.log(a);
}

Обратите внимание, что второй console.logсбой, вместо доступа к aизвне блока.


Не по теме: избегать загромождения глобального объекта ( window)

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

(function() {
    var a = 0; // `a` is NOT a property of `window` now

    function foo() {
        alert(a);   // Alerts "0", because `foo` can access `a`
    }
})();

В этом примере мы определяем функцию и выполняем ее сразу ( ()в конце).

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

TJ Crowder
источник
я могу window['a']=0сделать, чтобы было ясно, что я использую окно как карту? это windowособый таким образом, что некоторые браузеры не позволяют это и заставить меня использовать window.a?
Jayen
Одна заметка о # 3, которую, вероятно, стоит пояснить: window.a = 0;работает только в браузерных средах и только по соглашению. Связывание глобального объекта в переменную с именем windowне в ES Spec и поэтому не будет работать, например, V8 или Node.js, в то время как this.a = 0;(при вызове в глобальном контексте выполнения) , будет работать в любой среде , поскольку спецификации действительно указать что должно быть глобальный объект. Если вы переносите код в IIFE, как в разделе « Не по теме », вы можете передать его в качестве параметра с именем или получить прямую ссылку на глобальный объект. thiswindowglobal
Sherlock_HJ
@Sherlock_HJ: я добавил "в браузерах"; это также ранее в ответе, но я добавил его на тот случай, если люди перейдут к этому вопросу. Это в спецификации сейчас ; пока это только мимоходом, вы не найдете браузер, который этого не делает. Я немного удивлен , что это не в Приложении B .
TJ Crowder
@TJCrowder, Итак, глобальная переменная, объявленная с помощью var a = 0;автоматически, становится свойством глобального объекта. Если я объявлю var b = 0;в объявлении функции, будет ли это также свойство какого-то базового объекта?
Эспрессо
@ezpresso: Нет и да. Они становятся свойством объекта (в EnvironmentRecord из VariableEnvironment в исполнении ExecutionContext , где они появляются, подробность здесь и здесь ), но нет никакого способа , чтобы получить быстрый доступ , что объект из кода программы.
TJ Crowder
40

Сохраняя это простым:

a = 0

Код выше дает глобальную переменную области видимости

var a = 0;

Этот код даст переменную, которая будет использоваться в текущей области видимости и под ней

window.a = 0;

Обычно это то же самое, что глобальная переменная.

Умайр Джаббар
источник
Ваши заявления «Код выше дает глобальную переменную области видимости» и «Этот код даст переменную , которая будет использоваться в текущей области, а под ним» , вместе взятые, позволяют предположить , что вы не можете использовать первую строку и доступ a под текущая сфера Ты можешь. Кроме того, ваше использование «глобальной переменной» немного не подходит - два места, где вы говорите «глобальная переменная», не более глобально, чем место, где вы этого не говорите.
TJ Crowder
Само глобальное означает, что вы можете получить доступ / прочитать / записать переменную в любом месте, включая место, где я упомянул текущую область видимости, это так очевидно. И если вы предположите, что window.a и 'a' не будут глобальными в сценарии, то вы на 100% ошибаетесь.
Umair Jabbar
3
@ Umair: «глобальный сам по себе означает, что вы можете обращаться / читать / записывать переменную в любом месте» . Опять же, вы, кажется, называете первое и последнее более «глобальными», чем средние, что, конечно, не так.
TJ Crowder
4
средний считается, что используется внутри функции, все они будут одинаковыми, если используются в основной области видимости. использование var внутри функции было моим предположением
Umair Jabbar
4
@ Umair: "использование var внутри функции было моим предположением" А, ладно. Но это не вопрос. Вопрос очень четко говорит «в глобальном масштабе» . Если вы собираетесь изменить предположение (которое достаточно справедливо, чтобы расширить и объяснить более общий смысл), вам нужно будет четко понимать, что вы делаете в своем ответе.
TJ Crowder
10
<title>Index.html</title>
<script>
    var varDeclaration = true;
    noVarDeclaration = true;
    window.hungOnWindow = true;
    document.hungOnDocument = true;
</script>
<script src="external.js"></script>

/* external.js */

console.info(varDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(noVarDeclaration == true); // could be .log, alert etc
// returns false in IE8

console.info(window.hungOnWindow == true); // could be .log, alert etc
// returns true in IE8

console.info(document.hungOnDocument == true); // could be .log, alert etc
// returns ??? in IE8 (untested!)  *I personally find this more clugy than hanging off window obj

Есть ли глобальный объект, от которого все переменные зависают по умолчанию? Например: 'объявление globals.noVar'

Cody
источник
Очень хорошее исследование. Определенное руководство по использованию window.*декларации. Это объявление выглядит наиболее безопасным от копирования вашего кода, а также ясно.
Дан
7

Основано на превосходном ответе TJ Crowder : ( Не по теме: избегать беспорядкаwindow )

Это пример его идеи:

Html

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="init.js"></script>
    <script type="text/javascript">
      MYLIBRARY.init(["firstValue", 2, "thirdValue"]);
    </script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Hello !</h1>
  </body>    
</html>

init.js (на основании этого ответа )

var MYLIBRARY = MYLIBRARY || (function(){
    var _args = {}; // private

    return {
        init : function(Args) {
            _args = Args;
            // some other initialising
        },
        helloWorld : function(i) {
            return _args[i];
        }
    };
}());

script.js

// Here you can use the values defined in the html as if it were a global variable
var a = "Hello World " + MYLIBRARY.helloWorld(2);

alert(a);

Вот плнкр . Надеюсь, это поможет!

robe007
источник
5

В глобальном масштабе нет семантической разницы.

Но вы действительно должны избегать a=0 поскольку вы устанавливаете значение в необъявленную переменную.

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

(function() {
   // do stuff locally

   // Hoist something to global scope
   window.someGlobal = someLocal
}());

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

Как упомянул @AvianMoncellor, есть ошибка IE с var a = foo объявляется только глобальная область видимости файла. Это проблема пресловутого сломанного интерпретатора IE. Эта ошибка звучит знакомо, так что, вероятно, это правда.

Так что придерживайтесь window.globalName = someLocalpointer

Raynos
источник
2
«В глобальном масштабе нет семантической разницы». На самом деле, существует огромный семантическое различие, механизмы , с помощью которых свойство получает определенные совершенно разные - но с практической точки зрения она сводится лишь небольшой фактической разницы (в том , что вы не можете ). deletevar
TJ Crowder
@TJ Crowder Я этого не знал. Я думал, что объявление переменной устанавливает свойства объекта переменной. Не знал, что их нельзя удалить.
Raynos
Ага. Они также определены ранее, если вы используете var. Это просто совершенно разные механизмы, которые имеют практически одинаковый практический результат. :-)
TJ Crowder
@TJ Crowder Я забыл упомянуть, что varпрыгает до упора.
Raynos