Объявление переменных без ключевого слова var - PullRequest
45 голосов
/ 31 июля 2011

В w3schools написано:

Если вы объявляете переменную без использования «var», переменная всегда становится GLOBAL.

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

Ответы [ 7 ]

84 голосов
/ 31 июля 2011

Нет, нет никакой пользы от ОЗУ или чего-то в этом роде.

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

function foo() {
    var variable1, variable2;

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

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

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() {
    }
})();

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

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

16 голосов
/ 31 июля 2011

Побочные эффекты при забвении 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, автор Стоян Стефанов (О'Рейли).Copyright 2010 Yahoo !, Inc., 9780596806750.

3 голосов
/ 31 июля 2011

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

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

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

2 голосов
/ 31 июля 2011

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

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

1 голос
/ 31 января 2019

Объявление переменной внутри функции без использования 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);
}
Конечно, я уверен, что w3schools знает, что неявное глобальное объявление внутри функции не делается до ее вызова, но для тех, кто не знаком с javascript, это может быть и не так.ясно из предоставленной информации.

Что касается тонкостей предыдущих ответов, то после вызова функции 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);
1 голос
/ 31 июля 2011

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

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

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

0 голосов
/ 27 июня 2018

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

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

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

...