Різниця між синтаксисами декларування змінних у 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, я відчуваю це на власному веб-сайті. IE6, щоб бути конкретним. Я не зміг відобразити мій var enum, який знаходиться у зовнішньому файлі js, і я
посилаюсь на

@Ashwini У глобальному масштабі вікно - це глобальний об'єкт (у браузерах). var a = 1; console.log (a); 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.

У термінах специфікації, він створює ідентифікатор, що прив'язує до об'єкта Запис середовища для глобального середовища . Це робить його властивістю глобального об'єкта, оскільки глобальний об'єкт - це місце, де зберігаються прив'язки ідентифікаторів для об'єкта глобального середовища. Ось чому властивість не можна видалити: це не просто проста властивість, це прив'язка до ідентифікатора.

Прив'язка (змінна) визначається до запуску першого рядка коду (див. "Коли varце відбувається" нижче).

Зауважте, що в IE8 та новіших версіях властивість, створена на window, не перелічується (не відображається у for..inвиписках). В IE9, Chrome, Firefox та Opera це безліч.


№ 1.1 let a = 0;

Це створює глобальну змінну, яка не є властивістю глобального об'єкта. Це нова річ, що стосується ES2015.

В умовах специфікації, він створює ідентифікатор, прив'язуючи до декларативного запису середовища для глобального середовища, а не для об'єкта Environment Record. Глобальне середовище унікальна в тому , розкол по навколишньому середовищу записи, один для всіх старих речей , яка йде на глобальний об'єкт ( об'єкт 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глобальний, який посилається на глобальний об'єкт (у браузерах; деякі середовища, які не є браузером, мають еквівалентну глобальну змінну, наприклад, globalу NodeJS). Оскільки це звичайна властивість, ви можете її видалити.

Це властивість є перелічуваних, на 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виникнення іншого або а const aчи а var aє синтаксичною помилкою.

Ось приклад, що демонструє це letі constнабуває чинності негайно в їхньому блоці перед запуском будь-якого коду в цьому блоці, але він не доступний до оператора letабо 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`
    }
})();

У цьому прикладі ми визначаємо функцію і виконуємо її відразу ( ()в кінці).

Функція, що використовується таким чином, часто називається функцією масштабування . Функції, визначені в рамках функції масштабування, можуть отримати доступ до змінних, визначених у функції масштабування, оскільки вони закриваються над цими даними (див .: Закриття не є складним у моєму анемічному маленькому блозі).


чи можу я window['a']=0зробити це зрозумілим, що я використовую вікно як карту? це windowособливий таким чином, що деякі браузери не дозволяють це і змусити мене використовувати window.a?
Jayen

Одне зауваження №3, яке, мабуть, варто уточнити: window.a = 0;працює лише в середовищі браузера і лише за умовами. Зв'язування глобального об'єкта в змінну з ім'ям windowне в ES Spec і тому не буде працювати, наприклад, V8 або Node.js, в той час як this.a = 0;(при виклику в глобальному контексті виконання) , буде працювати в будь-якому середовищі , оскільки специфікації дійсно вказати що має бути глобальний об'єкт. Якщо ви загортаєте свій код у IIFE, як у розділі Off-topic , ви можете передавати як параметр з ім'ям або отримати пряме посилання на глобальний об’єкт. thiswindowglobal
Sherlock_HJ

@Sherlock_HJ: я додав "у браузерах"; це є і раніше у відповіді, але я додав це, якщо люди пропускають до цього. Це зараз у специфікації ; хоча це лише мимохідь, ви не знайдете браузера, який би це не зробив. Я трохи здивований , що це не в Додатку B .
TJ Crowder

@TJCrowder, Отже, глобальна змінна, оголошена символом var a = 0;автоматично, стає властивістю глобального об'єкта. Якщо я оголошу var b = 0;в межах декларації функції, чи буде це також властивість якогось базового об'єкта?
ezpresso

@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 заява"


Дуже приємна розвідка. Певне керівництво по використанню window.*декларації. Ця декларація виглядає найбільш захищеною від копіювання коду, а також зрозумілою.
День

7

На основі чудової відповіді TJ Crowder : ( Off-topic: Уникайте захаращення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);

Ось plnkr . Сподіваюся, це допоможе!


5

У глобальному масштабі немає смислової різниці.

Але вам слід справді уникати, a=0оскільки ви встановили значення для незадекларованої змінної.

Також використовуйте закриття, щоб уникнути редагування глобальної області застосування

(function() {
   // do stuff locally

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

Завжди використовуйте закриття та завжди піднімайтеся до глобальної сфери, коли це абсолютно необхідно. У більшості випадків ви повинні використовувати асинхронну обробку подій для більшості свого спілкування.

Як зазначав @AvianMoncellor, існує помилка IE, яка var a = fooлише оголошує глобальну область для файлу. Це проблема із сумнозвісною розбитою інтерпретацією IE. Ця помилка здається знайомою, тому, ймовірно, це правда.

Тож дотримуйтесь window.globalName = someLocalpointer


2
"У глобальному масштабі немає смислової різниці". Насправді існує величезна смислова різниця, механізми, за допомогою яких визначається властивість, абсолютно різні - але на практиці це зводиться до лише невеликої фактичної різниці (в тому, що ви не deleteможете var).
TJ Crowder

@TJ Crowder Я цього не знав. Я думав, що змінна декларація встановлює властивості на об'єкт змінної. Не знав, що їх неможливо видалити.
Райнос

Так. Вони також визначені раніше, якщо ви використовуєте var. Вони просто абсолютно різні механізми, які мають практично однаковий практичний результат. :-)
TJ Crowder

@TJ Crowder Я забув згадати, що varстрибає до зупинки сфери.
Райнос
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.