Питання Яка сфера змінних в JavaScript?


Яка сфера змінних в javascript? Чи є вони в тій же обсязі всередині, на відміну від поза функції? Чи це навіть має значення? Крім того, де зберігаються змінні, якщо вони визначені глобально?


1715
2018-02-01 08:27


походження


Ось ще один приємний посилання пам'ятати про цю проблему: "Пояснення обсягу та закриття JavaScript". - Full-Stack Software Engineer
Ось стаття, яка пояснює це дуже добре. Все, що вам потрібно знати про змінну область Javascript - Saurab Parakh
The раніше згадувалося Електронна книга Кайла Сімпсона доступна для читання на Github, і це розповідає вам все, що вам потрібно знати про JavaScript Scopes & Closures. Ви можете знайти його тут: github.com/getify/You-Dont-Know-JS/blob/master/... Це частина "Ви не знаєте JS" книжкові серії, що чудово підходить для всіх, хто хотів би більше дізнатися про JavaScript. - 3rik82


Відповіді:


Я думаю про те, що я можу зробити, це дати вам цілий ряд прикладів для вивчення. Програмісти Javascript практично класифікуються наскільки добре вони розуміють область застосування. Вона іноді може бути досить інтуїтивно зрозумілою.

  1. Глобально-орієнтована змінна

    // global scope
    var a = 1;
    
    function one() {
      alert(a); // alerts '1'
    }
    
  2. Локальний обсяг

    // global scope
    var a = 1;
    
    function two(a) {
      // local scope
      alert(a); // alerts the given argument, not the global value of '1'
    }
    
    // local scope again
    function three() {
      var a = 3;
      alert(a); // alerts '3'
    }
    
  3. Проміжний: Немає такої речі, як область блокування в JavaScript (ES5, ES6 вводить let)

    a.

    var a = 1;
    
    function four() {
      if (true) {
        var a = 4;
      }
    
      alert(a); // alerts '4', not the global value of '1'
    }
    

    б

    var a = 1;
    
    function one() {
      if (true) {
        let a = 4;
      }
    
      alert(a); // alerts '1' because the 'let' keyword uses block scoping
    }
    
  4. Проміжний: Властивості об'єкта

    var a = 1;
    
    function Five() {
      this.a = 5;
    }
    
    alert(new Five().a); // alerts '5'
    
  5. Розширений: Закриття

    var a = 1;
    
    var six = (function() {
      var a = 6;
    
      return function() {
        // JavaScript "closure" means I have access to 'a' in here,
        // because it is defined in the function in which I was defined.
        alert(a); // alerts '6'
      };
    })();
    
  6. Розширений: Прототип-базована сфера вирішення

    var a = 1;
    
    function seven() {
      this.a = 7;
    }
    
    // [object].prototype.property loses to
    // [object].property in the lookup chain. For example...
    
    // Won't get reached, because 'a' is set in the constructor above.
    seven.prototype.a = -1;
    
    // Will get reached, even though 'b' is NOT set in the constructor.
    seven.prototype.b = 8;
    
    alert(new seven().a); // alerts '7'
    alert(new seven().b); // alerts '8'
    

  7. Global + Local: Додатковий складний випадок

    var x = 5;
    
    (function () {
        console.log(x);
        var x = 10;
        console.log(x); 
    })();
    

    Це буде надруковано undefined і 10 а не 5 і 10 оскільки JavaScript завжди переміщує декларації змінних (не ініціалізацій) до верхньої частини області дії, що робить код еквівалентним:

    var x = 5;
    
    (function () {
        var x;
        console.log(x);
        x = 10;
        console.log(x); 
    })();
    
  8. Вимірювана змінна, що має обмеження за промовчанням

    var e = 5;
    console.log(e);
    try {
        throw 6;
    } catch (e) {
        console.log(e);
    }
    console.log(e);
    

    Це буде надруковано 5, 6, 5. Всередині пункту вилучення e тіні глобальних та локальних змінних. Але ця спеціальна сфера полягає лише в отриманій змінній. Якщо ти пишеш var f; всередині поля лову, то це точно так само, як якщо б ви визначили його перед блоком try-catch або після нього.


2267
2018-02-01 08:58



Не навіть близько до того, щоб бути всеосяжним, але це, можливо, обов'язковий набір набору трюків Javascript потрібно ефективно навіть READ сучасний JavaScript. - Triptych
Високо оцінена відповідь, не знаю чому. Це всього лише безліч прикладів без належного пояснення, то, здається, сплутає зразок прототипу (тобто розв'язування властивостей) з ланцюгом масштабу (наприклад, змінним дозволом). Комплексне (і точне) пояснення обсягу та властивості рішення знаходиться в comp.lang.javascript Поширені запитання. - RobG
@RobG Він високо оцінений, оскільки він корисний і зрозумілий для широкого кола програмістів, незважаючи на невеликий катаршрез. Посилання, яке ви опублікували, корисно для деяких професіоналів, незрозуміло для більшості людей, що пишуть Javascript сьогодні. Не соромтеся виправити будь-які проблеми з номенклатурою, редагуючи відповідь. - Triptych
@ triptych-я лише редагую відповіді, щоб виправити незначні речі, а не основні. Зміна "області" до "властивості" виправлятиме помилку, але не питання змішування спадщини та масштабу без чіткої різниці. - RobG
Якщо ви визначаєте змінну в зовнішній області, а потім мати вказівку if визначає змінну всередині функції з тим самим ім'ям навіть якщо це не досягне гілки це перевизначено Приклад - jsfiddle.net/3CxVm - Chris S


Javascript використовує діапазони ланцюгів, щоб визначити область для заданої функції. Існує, як правило, один глобальний обсяг, і кожна визначена функція має свою вбудовану область. Будь-яка функція, визначена в іншій функції, має локальний обсяг, який пов'язаний із зовнішньою функцією. Це завжди позиція в джерелі, яка визначає область застосування.

Елемент в ланцюжку масштабу - це, в основному, карта з покажчиком на батьківську область.

При вирішенні змінної, JavaScript починається з найглибшої області пошуку та пошуку назовні.


219
2018-02-01 08:35



Сфера застосування є ще одним терміном для [пам'яті] Закриття... для тих, хто читає тут, щоб вчитися / потрапити в javascript. - New Alexandria


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

(Я впевнений, що існує безліч тонкощів, які реальні програмісти JavaScript зможуть вказати на інші відповіді. Особливо я зіткнувся ця сторінка про що саме this означає в будь-який час. Сподіватися це більш вступний зв'язок достатньо, щоб вас почали, хоча.)


93
2018-02-01 08:31



Боюсь, навіть почати відповідати на це питання. Як реальний програміст Javascript, я знаю, як швидко відповідь може вийти з ладу. Красиві статті. - Triptych
@Триптих: Я знаю, що ви маєте на увазі, коли виходить з ладу, але будь ласка Додайте відповідь у будь-якому випадку. Я отримав вищесказане лише від виконання пари пошуків ... відповідь написана людиною з реальним досвідом пов'язаний бути кращим. Будь ласка, виправте будь-яку мою відповідь, що, безумовно, є неправильним, хоча! - Jon Skeet
Чомусь Джон Скет відповідає за МОЯ найпопулярнішу відповідь на переповнення стека. - Triptych


Стара школа JavaScript

Традиційно JavaScript дійсно має лише два види обсягів:

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

Я не буду детально про це, тому що вже є багато інших відповідей, що пояснюють різницю.


Сучасний JavaScript

The найновіші специфікації JavaScript тепер також дозволить третю сферу:

  1. Область блокування : Змінні відомі в межах блоквони оголошуються в, від моменту, коли вони оголошені на майбутнє (**)

Як створити змінні величини блоку?

Традиційно ви створюєте такі змінні, як:

var myVariable = "Some text";

Змінні блочного поля створюються так:

let myVariable = "Some text";

Отже, яка різниця між функціональною сферою та блоком обсягу?

Щоб зрозуміти різницю між функціональним обсягом та об'ємом блоків, розглянемо наступний код:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Тут ми бачимо, що наша змінна j відомо лише в першому для петлі, але не раніше і пізніше. Тим не менш, наша змінна i відомий у всій функції.

Крім того, врахуйте, що блочні обчислювальні змінні невідомі, перш ніж вони оголошуються, оскільки вони не піднімаються. Вам також не дозволяється повторно вводити ту ж саму блоку, що містять блоки. Це робить блоковані змінні менш сприйнятливими до помилок, ніж глобально або функціонально орієнтовані змінні, які піднімаються і не створюють жодних помилок у випадку кількох декларацій.


Чи безпечно використовувати сьогодні змінні величини блоків?

Незалежно від того, чи можна безпечно користуватися сьогодні, залежить ваше оточення:

  • Якщо ви пишете серверний код JavaScript (Node.js), ви можете спокійно використовувати let заява

  • Якщо ви пишете код JavaScript на стороні клієнта та використовуєте трансплікатор (наприклад, Traceur), ви можете спокійно використовувати let висловлювання, однак ваш код, імовірно, буде нічим іншим, але оптимальним щодо продуктивності.

  • Якщо ви пишете код JavaScript на стороні клієнта та не використовуєте перешивач, вам слід розглянути підтримку веб-переглядача.

    Сьогодні, 23 лютого 2016 року, це деякі веб-переглядачі, які або не підтримують let або мають лише часткову підтримку:

    • Internet Explorer 10 і нижче (без підтримки)
    • Firefox 43 і нижче (без підтримки)
    • Safari 9 і нижче (без підтримки)
    • Opera mini 8 і нижче (без підтримки)
    • Браузер Android 4 і нижче (без підтримки)
    • Опера 36 і нижче (часткова підтримка)
    • Chome 51 і нижче (часткова підтримка)

enter image description here


Як відстежувати підтримку браузера

Для оновленого огляду, які браузери підтримують let Заява на момент прочитання вашої відповіді, див це Can I Use сторінка.


(*) Глобальні та функціонально визначені змінні можна ініціалізувати та використовувати перед тим, як вони будуть оголошені, оскільки змінні JavaScript піднятий. Це означає, що декларації завжди значно перевищують масштаби.

(**) Блокуються змінні не піднімаються


55
2018-02-23 18:51



"НЕ Є відомо" - це вводить в оману, тому що змінна оголошена там за рахунок підйому. - Oriol
Наведений вище приклад є помилковим, змінні 'i' та 'j' не відомі поза блоком. "Нехай" змінні мають область лише в тому конкретному блоці, що не знаходиться поза блоком. Нехай також має інші переваги, ви не можете повторно ввести змінну знову, і це тримає лексичну область. - zakir
@ Оріол: Нарешті я покращив свою відповідь і піднявши адресу. Дякую, що вказали, що моя відповідь потребує вдосконалення. Я зробив ще кілька поліпшень. - John Slegers
@JonSchneider: Правильно! Де я кажу "стара школа JavaScript", я вживаю розмову про ECMAScript 5, і де я маю на увазі "сучасний JavaScript", я займаюся ECMAScript 6 (інша назва - ECMAScript 2015). Однак я не думаю, що тут важливо детально ознайомитись з деталями, оскільки більшість людей просто хочуть дізнатись (1) якою є різниця між обсягом блоків та функціональним обсягом, (2) які браузери підтримують область блоків, і (3) чи безпечно використовувати сферу блокування сьогодні для будь-якого проекту, над яким вони працюють. Тому я зосередив свою відповідь на вирішення цих питань. - John Slegers
@JonSchneider: (продовження) Тим не менше, я просто додав посилання на статтю Smashing Magazine на ES6 / ES2015 для тих, хто хоче дізнатися більше про те, які функції були додані до JavaScript протягом останніх двох років ... будь-кого іншого, хто може бути цікаво, що я маю на увазі з "сучасним JavaScript". - John Slegers


Ось приклад:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Ви хочете вивчити закриття, і як їх використовувати приватні члени.


35
2018-02-01 08:48





Ключ, наскільки я розумію, полягає в тому, що Javascript має рівень обсягу функцій порівняно з найбільш поширеним обсягом блоку.

Ось хороша стаття з цього питання.


28
2018-05-15 17:38





У "Javascript 1.7" (розширення Mozilla до Javascript) можна також оголосити змінні блоків-сфер за допомогою let заява:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

23
2018-04-06 11:19



Так, але чи безпечно це використовувати? Я маю на увазі, я б реально вибрав цю реалізацію, якщо мій код буде працювати в WebKit? - Igor Ganapolsky
@Python: Ні, WebKit не підтримує let. - kennytm
Я думаю, єдиним дієвим використанням для цього буде, якщо б ви знали, що всі клієнти будуть використовувати браузер Mozilla, як для внутрішньої системи компаній. - GazB
Або якщо ви програмуєте за допомогою системи XUL, інтерфейсу Mozilla, де ви будуєте, використовуючи css, xml та javascript. - Gerard ONeill
@GazB навіть це жахлива ідея! Отже, сьогодні ви знаєте, що ваші клієнти використовують Mozilla, після чого з'являється нове повідомлення, що тепер вони використовують щось інше. І.Е. причина, чому наша система заробітної плати засмоктує ... Ви повинні використовувати IE8 і ніколи не IE9 або IE10 або Firefox або Chrome, тому що він не буде працювати ... - buzzsawddog


Ідея обчислення в JavaScript, коли спочатку розроблена Брендан Ейч прийшов з HyperCard мова скриптів HyperTalk.

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

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

Це прив'язка вказує на містячий об'єкт. Інколи сфери чи контексти виконання змінюються без змінювача об'єкта, наприклад, у оголошеній функції, де може міститися об'єкт window або конструктор функція.

Ці контексти виконання створюються будь-яким контролем часу, який передано. Контроль переноситься, коли код починає виконуватись, і це, перш за все, відбувається з виконання функції.

Отже, це технічне пояснення. На практиці важливо пам'ятати, що в JavaScript

  • Області технічно є "Контексти виконання"
  • Контексти складають скупчення середовищ, де зберігаються змінні
  • Вершина стека має перевагу (нижня частина - глобальний контекст)
  • Кожна функція створює контекст виконання (але не завжди новий зв'язок)

Застосовуючи це до одного з попередніх прикладів (5. "Закриття") на цій сторінці, можна стежити за стеком контекстів виконання. У цьому прикладі в стекі є три контекста. Вони визначаються зовнішнім контекстом, контекстом у функції, що викликається негайно, називається var six, і контекстом у функції, що повертається, всередині var six's відразу викликаної функції.

я) Зовнішній контекст. Вона має змінну середовище a = 1
іі) Контекст IIFE має лексичне середовище a = 1, але змінне середовище a = 6, яке має перевагу в стеку
III) Повернутий контекст функції, він має лексичне середовище a = 6, і це значення, на яке посилається в сповіщенні, коли викликається.

enter image description here


18
2017-09-14 20:29





1) Існує глобальний обсяг, сферу функцій, а також області з і зловити. Взагалі, для змінної немає рівні "блоку" - з пропозиціями "з" і "зловити" додавати імена в їх блоки.

2) Scopes вкладені функціями до глобального масштабу.

3) Властивості вирішуються шляхом проходження прототипу ланцюга. За допомогою оператора з виразом властивостей об'єкта об'єкта в лексичну область, визначену блоком.

EDIT: ECMAAScript 6 (Harmony) призначений для підтримки let, і я знаю, що chrome дозволяє позначити "гармонію", і, можливо, це підтримує його.

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

РЕДАГУВАННЯ: виходячи з того, що Бенджамін вказав на коментарі та зловмисне повідомлення, я відредагував цю публікацію та додав більше. Обидва повідомлення "with" і "catch" вводять змінні в їхні відповідні блоки, і це є блоковий діапазон. Ці змінні, крім властивостей переданих в них об'єктів, також збігаються.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

EDIT: Пояснювальний приклад:

Тест 1 обчислюється до блоку з блоком, але з псевдонімом до a.test1. 'Var test1' створює нову змінну test1 у верхньому лексичному контексті (функція або глобальна), якщо вона не є властивістю a - яка вона є.

Yikes! Будьте обережні, використовуючи 'with' - точно так само, як var - це ноуп, якщо змінна вже визначена у функції, це також ноуп щодо імен, імпортованих з об'єкта! Трохи піднімається на ім'я, яке вже визначається, зробить це набагато безпечніше. Через це особисто я ніколи не буду користуватися.


16
2017-10-25 00:41



У вас є якісь помилки тут, адже один JavaScript має форми обчислення блоків. - Benjamin Gruenbaum
Мої вуха (очі) відкриті, Бенджамін - Мої викладені вище вказують на те, як я обробляв область визначення Javascript, але вони не базуються на читанні специфікації. І я сподіваюсь, що ви не мали на увазі твердження з (з формою обєктування об'єктів) або спеціальним синтаксисом Mozilla 'let'. - Gerard ONeill
Добре, with заява є форма обчислення блоку але catch Застереження є набагато більш поширеною формою (Fun fact, v8 implements catch з with) - це в основному єдині форми визначення розміру блоків в самому JavaScript (тобто функція, глобальна, спроба / зловмисник та їх похідні), однак хостові середовища мають різні поняття обсягу, наприклад, вбудовані події в браузері та NodeJS vm модуль - Benjamin Gruenbaum
Бенджамін - з того, що я бачу, як з, так і зловити, вводять об'єкт лише в поточний об'єкт (і, отже, властивості), але потім після закінчення відповідного блоку змінні скидаються. Але, наприклад, нова змінна, введена в лову, матиме обсяг функції / методу, що додають. - Gerard ONeill
Який саме те, що означає блок обсягу значень :) - Benjamin Gruenbaum