Объявление переменных без ключевого слова var

В w3schools написано:

Если вы объявляете переменную без использования "var", переменная всегда становится ГЛОБАЛЬНОЙ.

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


person xralf    schedule 31.07.2011    source источник


Ответы (8)


Нет, нет никакой выгоды от оперативной памяти или чего-то в этом роде.

То, о чем говорит w3schools, я называю Ужасом. неявных глобальных переменных. Рассмотрим эту функцию:

function foo() {
    var variable1, variable2;

    variable1 = 5;
    varaible2 = 6;
    return variable1 + variable2;
}

Выглядит достаточно просто, но возвращает NaN, а не 11 из-за опечатки в строке varaible2 = 6;. И он создает глобальную переменную с именем с опечаткой:

function foo() {
    var variable1, variable2;

    variable1 = 5;
    varaible2 = 6;
    return variable1 + variable2;
}
console.log(foo());     // NaN
console.log(varaible2); // 6?!?!?!

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

Это просто особенность свободного режима JavaScript, присвоение полностью необъявленного идентификатора не является ошибкой; вместо этого он правильно создает глобальный объект, а свойства глобального объекта являются глобальными переменными. (Вплоть до ES5 все глобальные переменные были свойствами глобального объекта. Однако начиная с ES2015 был добавлен новый тип глобальных переменных, который не является свойством глобального объекта. Global-scope let, const и class создают новые какая-то глобальная)

Мой пример — опечатка, но вы, конечно, могли бы сделать это специально, если бы захотели. В конце концов, это четко определенная часть языка. Так:

myNewGlobal = 42;

... везде, где не объявлено myNewGlobal, будет создан новый глобальный.

Но я настоятельно рекомендую никогда не делать это специально: это затрудняет чтение и поддержку кода, и этот код будет несовместим с модулями JavaScript, когда они станут более распространенными и широко распространенными. Если вам действительно нужно создать глобальную переменную внутри функции во время выполнения (уже красный флаг, но для этого есть веские причины), сделайте это явно, назначив свойство на window (или что-то еще, что ссылается на глобальный объект в вашем среде; это window в браузерах):

window.myNewGlobal = 42;

На самом деле, я бы предложил использовать строгий режим ES5< /эм>. Строгий режим делает присвоение необъявленного идентификатора ошибкой, а не молча создает глобальный идентификатор. Если бы мы использовали строгий режим, проблему с foo выше было бы намного проще диагностировать:

"use strict"; // Turns on strict mode for this compilation unit

function foo() {
    var variable1, variable2;

    variable1 = 5;
    varaible2 = 6;                 // <=== ReferenceError
    return variable1 + variable2;
}
console.log(foo());


Несколько тангенциально, но в целом я бы рекомендовал избегать глобальных переменных, где это возможно. Глобальное пространство имен уже очень и очень загромождено в браузерах. Браузер создает глобал для каждого элемента в DOM с id, для большинства элементов с name и имеет несколько собственных предопределенных глобалов (например, title), которые могут легко конфликтовать с вашим кодом.

Вместо этого просто определите себе хорошую функцию области видимости и поместите в нее свои символы:

(function() {
    var your, symbols, here, if_they_need, to_be_shared, amongst_functions;

    function doSomething() {
    }

    function doSomethingElse() {
    }
})();

И если вы это сделаете, вы можете включить строгий режим:

(function() {
    "use strict";
    var your, symbols, here, if_they_need, to_be_shared, amongst_functions;

    function doSomething() {
    }

    function doSomethingElse() {
    }
})();

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

Обратите внимание, что в модуле JavaScript (добавленном в ES2015, но только сейчас начинающем находить свой путь в дикой природе) строгий режим включен по умолчанию. (Это также относится к определениям class, также новым в ES2015.)

person T.J. Crowder    schedule 31.07.2011
comment
Хотя я согласен с тем, что мне НЕ НРАВИТСЯ глобалы, ошибки нет. Ваш код просто возвращает x + y как 11, как и предполагалось. - person Roland; 03.04.2018
comment
@Roland: слово «ошибка» не появляется в ответе, не совсем уверен, что вы имеете в виду. Но неявные глобальные переменные часто создаются случайно (опечатки), что определенно приводит к ошибкам. Это часть того, почему я использую строгий режим (и/или инструменты lint). - person T.J. Crowder; 03.04.2018
comment
Если ошибки нет, программа работает нормально. Проблема с глобальными переменными носит ментальный характер. Я пришел сюда во время отладки реальной проблемы и задавался вопросом, не вызвала ли ее необъявленная переменная, теперь я знаю, что проблема была не в этом. - person Roland; 03.04.2018
comment
@Roland: Ну, я рад, что твоя проблема была не в этом. Для многих людей это так, потому что они случайно присваивают ietm вместо item или аналогичного, и нигде нет никаких указаний на это (кроме неработающего кода). - person T.J. Crowder; 03.04.2018
comment
Мне потребовалось много умственных усилий, чтобы понять, что длинный ответ и ссылка на w3schools НЕ говорят, что необъявленная переменная является ошибкой сама по себе. Ваше наблюдение за орфографическими ошибками и вероятность ошибок (ошибок) должно быть достаточным ответом. - person Roland; 03.04.2018
comment
@Roland: я решил пересмотреть ответ в свете комментариев за последние несколько лет, включая ваши. - person T.J. Crowder; 05.04.2018
comment
Вы подчеркнули Ужас опечаток. Кстати, однажды мне пришлось поддерживать код, в котором кто-то объединил оба ужаса, преднамеренно введя опечатку, чтобы отключить строку кода, вместо того, чтобы комментировать. - person Roland; 06.04.2018

Побочные эффекты при забывании var

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

• Глобальные переменные, созданные с помощью var (созданные в программе вне какой-либо функции), не могут быть удалены.

• Подразумеваемые глобальные переменные, созданные без var (независимо от того, созданы ли они внутри функций), могут быть удалены.

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

// define three globals
var global_var = 1;
global_novar = 2; // antipattern
(function () {
   global_fromfunc = 3; // antipattern
}());
// attempt to delete
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true
// test the deletion
typeof global_var; // "number"
typeof global_novar; // "undefined"
typeof global_fromfunc; // "undefined"

В строгом режиме ES5 присваивание необъявленным переменным (например, двум антишаблонам в предыдущем фрагменте) вызовет ошибку.

Шаблоны JavaScript, Стоян Стефанов (O’Reilly). Copyright 2010 Yahoo!, Inc., 9780596806750.

person user278064    schedule 31.07.2011
comment
необходима небольшая модификация //типа переменной result. Когда я попытался запустить вышеуказанное объявление переменной в компиляторе w3school, я получил предупреждение (typeof global_var); // число оповещений (typeof global_novar); // числовое оповещение (тип global_fromfunc); // не определено - person Anurag_BEHS; 19.06.2016
comment
@Anurag_BEHS - не уверен, какой именно код вы ввели в w3schools tryit, чтобы получить номер для global_novar, но я только что проверил там и получил тот же результат, что и в ответе. Я рекомендую alert(delete global_novar); - это возвращает true или false? Если он возвращает false, значит, вы сделали что-то отличное от того, что показано в этом ответе. - person ToolmakerSteve; 15.10.2019
comment
Re подразумеваемые глобальные переменные технически не являются реальными переменными, но они являются свойствами глобального объекта. Я бы описал это иначе. Как переменная, используемая в глобальной области видимости, так и подразумеваемые глобальные переменные прикрепляют свойства к объекту окна. Единственное отличие (если объявление var находится в глобальной области видимости, а не внутри функции) состоит в том, что при использовании var свойство имеет configurable: false. оператор удаления MDN. - person ToolmakerSteve; 15.10.2019
comment
Однако это не единственная разница. Переменные, объявленные с помощью var в глобальном контексте, ведут себя как переменные (например, подъем), тогда как свойства глобального объекта ведут себя... ну, как свойства. Это две разные вещи. - person Ben Aston; 14.04.2020

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

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

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

person Guffa    schedule 31.07.2011
comment
Странно, что javascript допускает конструкции, которые бесполезны, но могут вызвать проблемы только при опечатке. - person xralf; 31.07.2011
comment
@xralf: все языки позволяют использовать конструкции, которые можно использовать неправильно. while (true); приходит на ум. - person T.J. Crowder; 31.07.2011

Объявление переменной внутри функции без использования var, let или const внутри функции не более полезно, чем объявление этой переменной с помощью var, let или const. И, как отмечалось в предыдущих ответах на этот вопрос, локальные неявные глобальные объявления функций могут сбивать с толку и вызывать проблемы вне области действия функции, в которой они были объявлены.

Я хотел бы поговорить о некоторых тонкостях, которые отсутствуют в цитате w3schools и в предыдущих ответах на этот вопрос.

Прежде всего, если вы никогда не вызываете функцию, генерирующую неявные глобальные переменные, вы не будете генерировать никакие неявные глобальные переменные. Это тонкое отличие от цитаты w3schools, потому что оно бросает вызов разделу «всегда» их утверждения.

function generateImplicitGlobals(){
  x = "x";
  window.y = "y";
}

// before calling the generateImplicitGlobals function, we can safely see that the x and y properties of the window object are both undefined:
console.log("before calling the generateImplicitGlobals function, properties x and y of the window object are: " + window.x + " and " + window.y);

// before calling the generateImplicitGlobals function, we can test for the existence of global variables x and y; note that we get errors instead of undefined for both.
try{
  console.log("before calling the generateImplicitGlobals function, x is: " + x);
}
catch(e){
  console.log("before calling the generateImplicitGlobals function, an attempt to reference some global variable x produces " + e);
}

try{
  console.log("before calling the generateImplicitGlobals function, y is: " + y);
}
catch(e){
  console.log("before calling the generateImplicitGlobals function, an attempt to reference the global variable b also produces " + e);
}
Admittedly, I am sure that w3schools is aware that the implicit global declaration inside a function isn't made before the function is called, but, for folks who are new to javascript, it may not be clear from the given information.

Что касается тонкостей предыдущих ответов, после вызова функции generateImplicitGlobals мы видим, что попытки доступа либо к свойству window.x, либо к глобальной переменной x возвращают одни и те же значения (и что свойство window.y и глобальная переменная y возвращают одинаковые значения). Эти операторы верны при вызове внутри или вне функции generateImplicitGlobals.

function generateImplicitGlobals(){
  x = "x";
  window.y = "y";
  console.log("inside the function, x and window.x are: " + x + " and " + window.x);
  console.log("inside the function, y and window.y are: " + y + " and " + window.y);
}

// now, call the generator, and see what happens locally and globally.
generateImplicitGlobals();
console.log("after calling the generateImplicitGlobals function, x, window.x, y, and window.y are: " + x + ", " + window.x + ", " + y + ", and " + window.y);

person Ed_Johnsen    schedule 31.01.2019

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

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

person brezanac    schedule 31.07.2011

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

Затем, когда вы измените значение глобального параметра, вы перезапишете его значение.

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

person QuentinUK    schedule 31.07.2011
comment
Кто-то другой, возможно, это вы, но вы забыли, что уже использовали это имя где-то еще. - person QuentinUK; 31.07.2011
comment
Или даже то, что у вас есть элемент на вашей странице, использующий этот id, поскольку почти все браузеры отбрасывают их на объект window. (Я думаю, что Firefox — единственный противник.) - person T.J. Crowder; 31.07.2011

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

Как упоминалось выше, вы можете совершить ошибку, просто неправильно написав свои переменные, и решением является ключевое слово "use strict";
Если это ключевое слово объявлено, вы получите ошибку: Uncaught ReferenceError: foo is not defined.

Это также относится к защищенному коду:
1. При написании защищенного кода мы не хотим, чтобы к нашим переменным обращались где-либо, кроме того места, где они были фактически объявлены. Не объявляйте глобальные переменные без надобности.
2. Всегда внимательно читайте предупреждения и устраняйте их. Используйте "use strict";, JSlint и другие инструменты, чтобы увидеть и устранить предупреждение, чтобы сделать ваш код лучше.

person Erik Rybalkin    schedule 26.06.2018

Для чего стоит переменные могут быть добавлены к списку аргументов в функции.

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

Присвоение нового значения именованному аргументу временно заменяет его для текущей области (и производных областей).

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

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


  function noVars(a1,/*vars=*/v1,v2,v3) {
    if (noVars.lastA1===a1) return noVars.lastAnswer;

    noVars.lastA1=a1;

    v1=a1*a1;
    v2=v1*v1;
    v3=v2*v2*v2;

    noVars.lastAnswer = a1+v1+v2+v3;

    return noVars.lastAnswer;
  }

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

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

это также будет работать:


  var noVars = function (a1,/*vars=*/v1,v2,v3) {
    if (noVars.lastA1===a1) return noVars.lastAnswer;

    noVars.lastA1=a1;

    v1=a1*a1;
    v2=v1*v1;
    v3=v2*v2*v2;

    noVars.lastAnswer = a1+v1+v2+v3;

    return noVars.lastAnswer;
  };

person unsynchronized    schedule 23.06.2021