Яка область змінних у JavaScript?


2011

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


4
Ось ще одне приємне посилання, щоб пам’ятати про це питання: « Пояснення сфери дії та закриття JavaScript ».
Повний стек-програмний інженер,

9
Ось стаття, яка це дуже добре пояснює. Все, що вам потрібно знати про змінні області Javascript
Saurab Parakh,

2
Згадувався раніше електронна книга Кайл Сімпсона доступна для читання на Github, і він говорить вам все , що вам потрібно знати про JavaScript Скоупс і ковпачках. Ви можете знайти його тут: github.com/getify/You-Dont-Know-JS/blob/master/… Це частина серії книг "Ви не знаєте JS" , що чудово підходить для всіх, хто хотів би знати докладніше про JavaScript.
3rik82

Відповіді:


2534

TLDR

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

Чотири сфери застосування:

  1. Глобальний - видно всім
  2. Функція - видима в межах функції (та її підфункцій та блоків)
  3. Блок - видимий всередині блоку (та його підблоків)
  4. Модуль - видно в модулі

Поза окремими випадками глобальної та модульної області, змінні оголошуються за допомогою var(область функцій), let(область блоку) та const(область блоку). Більшість інших форм декларації ідентифікатора мають жорсткий режим блоку.

Огляд

Область застосування - область кодової бази, над якою діє ідентифікатор.

Лексичне середовище - це відображення між іменами ідентифікаторів та пов'язаними з ними значеннями.

Область застосування складається з пов'язаного гніздування лексичних середовищ, причому кожен рівень вкладеності відповідає лексичному середовищу контексту виконання предків.

Ці пов'язані лексичні середовища утворюють сферу "ланцюга". Розв’язання ідентифікатора - це процес пошуку уздовж цього ланцюга для відповідного ідентифікатора.

Роздільна здатність ідентифікатора відбувається лише в одному напрямку: назовні. Таким чином, зовнішнє лексичне середовище не може "бачити" внутрішнє лексичне середовище.

У вирішенні сфери дії ідентифікатора в JavaScript є три важливі фактори :

  1. Як оголошено ідентифікатор
  2. Де оголошено ідентифікатор
  3. Незалежно від того, чи перебуваєте ви в суворому режимі або в не строгому режимі

Деякі із способів визначити ідентифікатори:

  1. var, letіconst
  2. Параметри функції
  3. Параметр блоку лову
  4. Декларації функцій
  5. Іменні вирази функцій
  6. Неявно визначені властивості глобального об'єкта (тобто відсутні varв нестрогому режимі)
  7. import заяви
  8. eval

Деякі з ідентифікаторів локацій можна оголосити:

  1. Глобальний контекст
  2. Функціональний орган
  3. Звичайний блок
  4. Верхня частина керуючої структури (наприклад, цикл, якщо, під час тощо)
  5. Орган управління структурою
  6. Модулі

Стилі декларації

вар

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

хай і const

Ідентифікатори, задекларовані за допомогою letта const мають блок-сферу , за винятком випадків, коли вони оголошені безпосередньо в глобальному контексті, і в цьому випадку вони мають глобальну сферу застосування.

Примітка: let, constі var все піднімають . Це означає, що їх логічне положення визначення є вершиною сфери їх застосування (блоком або функцією). Однак змінні, оголошені використаними, letі constне можуть бути прочитані або призначені доти, поки контроль не передає точку декларації у вихідний код. Проміжний період відомий як часова мертва зона.

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

Імена параметрів функції

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

Декларації функцій

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

Іменні вирази функцій

Визначені вирази функцій визначаються для себе (наприклад, з метою рекурсії).

Неявно визначені властивості глобального об'єкта

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

eval

У evalрядках змінні, оголошені за допомогою, varбудуть розміщені в поточному діапазоні або, якщо evalвони використовуються опосередковано, як властивості глобального об'єкта.

Приклади

Нижче буде кидати ReferenceError , тому що імена x, yі zне має ніякого значення поза функцією f.

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

Нижче наведено ReferenceError для yта z, але не для x, оскільки видимість xне обмежена блоком. Блоки, що визначають тіла керуючих структур, як if, forі while, поводяться аналогічно.

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

Далі xвидно зовні циклу, оскільки varмає область функцій:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

... через таку поведінку вам потрібно бути обережним щодо закриття змінних, оголошених використанням varв циклі. Тут xзадекларований лише один екземпляр змінної , і він логічно розташований поза циклом.

Наступні друкуються 5п'ять разів, а потім 5шостий раз друкуються для console.logзовнішньої петлі:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

Наступні відбитки, undefinedоскільки xце блок-область. Зворотні дзвінки виконуються по черзі асинхронно. Нова поведінка для letзмінних означає , що кожна анонімна функція зімкнулася над іншою змінною з ім'ям x( в відміну від неї зробили б з var), і так цілих чисел 0через 4роздруковуються.:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

Наведене нижче НЕ кине а, ReferenceErrorтому що видимість xне обмежується блоком; однак вона буде надрукована, undefinedоскільки змінна не була ініціалізована (через ifвисловлювання).

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

Змінна, оголошена вгорі forциклу за допомогою let, прив'язується до тіла циклу:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

Наступне буде кидати a, ReferenceErrorоскільки видимість xобмежується блоком:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

Змінні, задекларовані за допомогою модулів var, letабо constвсі вони відносяться до:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

Далі буде оголошено властивість глобального об'єкта, оскільки змінні, оголошені з використанням varу глобальному контексті, додаються як властивості до глобального об'єкта:

var x = 1
console.log(window.hasOwnProperty('x')) // true

letі constв глобальному контексті не додають властивості до глобального об'єкта, але все ж мають глобальний обсяг:

let x = 1
console.log(window.hasOwnProperty('x')) // false

Параметри функції можна вважати оголошеними в тілі функції:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Параметри блоку лову відносяться до тіла лову блоку:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

Ім'яні вирази функцій присвоюються лише самому виразу:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

У не строгому режимі неявно визначені властивості глобального об'єкта мають загальний обсяг. У суворому режимі ви отримуєте помилку.

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

У не строгому режимі декларації функцій мають область функцій. У суворому режимі вони мають блок-сферу.

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

Як це працює під кришкою

Обсяг визначається як лексична область коду, над якою діє ідентифікатор.

В JavaScript, кожна функція-об'єкт має приховану [[Environment]]посилання , яка є посиланням на лексичну середу з контексті виконання (кадрі стеки) , в протягом якого він був створений.

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

Зауважимо, що цей зв’язок між новим контекстом виконання та лексичним середовищем об’єкта функції називається a закриттям .

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

Дізнайтеся більше .


280
Навіть не наближається до вичерпності, але це, мабуть, необхідний набір хитростей фокусу в області JavaScript, для того, щоб ефективно читати сучасний JavaScript.
Триптих

148
Високо оцінена відповідь, не знаю чому. Це лише купа прикладів без належного пояснення, а потім, схоже, плутає успадкування прототипу (тобто роздільну здатність властивості) з ланцюгом області застосування (тобто змінною роздільною здатністю). Вичерпне (та точне) роз'яснення сфери застосування та властивості властивостей міститься в примітках до поширених запитань comp.lang.javascript .
RobG

109
@RobG Це високо оцінено, оскільки він корисний і зрозумілий широкому колу програмістів, незважаючи на незначні катахрези. Посилання, яке ви опублікували, хоча корисне для деяких професіоналів, незрозуміле для більшості людей, які сьогодні пишуть Javascript. Сміливо виправляйте будь-які питання номенклатури, редагуючи відповідь.
Триптих

7
@ триптих - я редагую лише відповіді, щоб виправити незначні речі, а не основні. Зміна "сфери" на "властивість" виправить помилку, але не питання змішування спадщини та обсягу без дуже чіткого розрізнення.
RobG

24
Якщо ви визначите змінну у зовнішній області, а потім матимете оператор if, визначте змінну всередині функції з тим самим іменем, навіть якщо ця функція , якщо гілка не буде досягнута, буде повторно визначена. Приклад - jsfiddle.net/3CxVm
Chris S

233

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

Елементом ланцюга області є в основному Карта з вказівником на його батьківську область.

При вирішенні змінної javascript запускається в найпотужнішому обсязі і шукає назовні.


1
Ланцюги сфери застосування - ще один термін для [пам'яті] Закриття ... для тих, хто читає тут, щоб дізнатися / потрапити в JavaScript.
Нова Олександрія

108

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

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


7
Боюся навіть почати відповідати на це питання. Як справжній програміст Javascript, я знаю, як швидко відповідь могла вийти з рук. Приємні статті.
Триптих

10
@Triptych: Я знаю, що ви маєте на увазі про те, що речі виходять з рук, але будь-ласка, додайте відповідь у будь-якому випадку. Я отримав найвище робити кілька запитів ... відповідь написаний кимось - то з фактичним досвідом буде зобов'язаний бути краще. Будь ласка, виправте будь-яку мою відповідь, яка, безумовно, неправильна!
Джон Скіт

4
Чомусь Джон Скіт відповідає за МОЮ найпопулярнішу відповідь на переповнення стека.
Триптих

75

Стара школа JavaScript

Традиційно JavaScript дійсно має лише два типи сфери застосування:

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

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


Сучасний JavaScript

В останні специфікації 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-код на стороні клієнта та використовуєте транслятор на основі вузла (наприклад, скрипт оболонки прослідковування або Babel ), ви можете сміливо використовувати letоператор. Оскільки ваш браузер знатиме лише про перекладений код, недоліки в роботі повинні бути обмеженими.

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

    Ось такі браузери, які взагалі не підтримують let:

    • Internet Explorer 10 і нижче
    • Firefox 43 і нижче
    • Сафарі 9 і нижче
    • Android браузер 4 і нижче
    • Опера 27 і нижче
    • Чом 40 і нижче
    • БУДЬ-яка версія браузера Opera Mini & Blackberry

введіть тут опис зображення


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

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


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


2
"НЕ відомий" вводить в оману, тому що змінна оголошена там через підйом.
Оріол

Наведений вище приклад вводить в оману, змінні 'i' та 'j' не відомі поза блоком. Змінні 'Let' мають сферу застосування лише в тому конкретному блоці, що не знаходиться поза блоком. Нехай також є й інші переваги, ви не можете повторно декларувати змінну, і вона зберігає лексичну область.
закір

1
Це було корисно, дякую! Думаю, було б ще корисніше бути конкретним щодо того, що ви маєте на увазі під «Сучасним JavaScript» та «Стара школа JavaScript»; Я думаю, що вони відповідають як ECMAScript 6 / ES6 / ECMAScript 2015, так і більш ранній версії відповідно?
Джон Шнайдер

1
@JonSchneider: Правильно! Де я кажу "старий шкільний JavaScript", я не хочу говорити про ECMAScript 5 і де я переходжу до "сучасного JavaScript", я беру про ECMAScript 6 (він же ECMAScript 2015). Я не думав, що насправді так важливо детально розібратися тут, хоча, як більшість людей просто хоче знати (1) яка різниця між областю блоку та функціональною областю, (2) які браузери підтримують область блоку та (3) чи безпечно сьогодні використовувати блок-блок для будь-якого проекту, над яким вони працюють. Тому я зосередив свою відповідь на вирішенні цих питань.
John Slegers

1
@JonSchneider: (продовження) Тим не менш, я просто додав посилання на статтю Smashing Magazine про ES6 / ES2015 для тих, хто хоче дізнатися більше про те, які функції були додані до JavaScript протягом останніх кількох років ... усіх, хто ще може бути цікаво, що я маю на увазі під "сучасним JavaScript".
John Slegers

39

Ось приклад:

<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>

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



26

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

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

2
Так, але це безпечно у використанні? Я маю на увазі, чи реально я б обрав цю реалізацію, якщо мій код буде працювати в WebKit?
ІгорГанапольський

10
@Python: Ні, WebKit не підтримує let.
kennytm

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

Або якщо ви програмуєте за допомогою фреймворку XUL, рамки інтерфейсу Mozilla, де ви будуєте, використовуючи css, xml та javascript.
Джерард ONeill

1
@GazB навіть це жахлива ідея! Отже, сьогодні ви знаєте, що ваші клієнти використовують Mozilla, а потім виходить нова примітка, в якій зазначається, що зараз вони використовують щось інше. IE тому, що наша система оплати відстій ... Ви повинні використовувати IE8, а ніколи IE9 або IE10 або Firefox чи Chrome, тому що вона не працює, звичайно ...
buzzsawddog

25

Ідея розбору в JavaScript, коли оригінально розроблений Брендан Ейх, виникла з сценарію мови HyperCard HyperTalk .

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

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

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

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

Так це технічне пояснення. На практиці важливо пам’ятати про це в JavaScript

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

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

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

введіть тут опис зображення


17

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

2) Області застосування вкладені функціями аж до глобальної сфери.

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

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

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

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

 //chrome (v8)

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

EDIT: Уточнюючий приклад:

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

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


Тут у вас є деякі помилки, оскільки один JavaScript має форми блокового визначення.
Бенджамін Грюнбаум

Мої вуха (очі) відкриті, Бенджамін - Мої твердження вище - це те, як я ставлюся до визначення Javascript, але вони не ґрунтуються на читанні специфікації. І я сподіваюся, що ви не посилаєтесь на оператор with (який є формою обстеження об'єктів) або на спеціальний синтаксис "пускати" Mozilla.
Джерард ONeill

Ну, withоператор - це форма визначення рівня блоку, але catchпропозиції є набагато більш поширеною формою (Fun Fun, v8 реалізується catchз a with) - це майже єдині форми блокового обстеження в самому JavaScript (Тобто функція, глобальна, спробуйте / ловити , з та їх похідними), проте хост-середовища мають різні поняття про масштабування - наприклад, вбудовані події в браузері та vm-модулі NodeJS.
Бенджамін Грюнбаум

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

2
Що саме означає показник блоку :)
Бенджамін Грюнбаум

9

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

Спробуйте цю функцію за адресою:

Дивіться демонстрацію на веб-сайті:

Переглянути код за адресою:

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


1
Не працює для мене з Firefox 26. Я вставляю код або завантажую файл, натискаю на Execute, і нічого не відбувається.
mplwork

Сфера застосування та успадкування - це дві різниці.
Бен Астон

9

Діапазон JavaScript має лише два типи:

  1. Глобальний обсяг : Глобальний - це не що інше, як область віконного рівня. Ось, змінна присутність у всій програмі.
  2. Функціональний обсяг : Змінна, оголошена у функції з varключовим словом, має функціональну область.

Щоразу, коли викликається функція, створюється об'єкт змінної області (і включається в ланцюг області), за яким слідують змінні в JavaScript.

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Область застосування ->

  1. Рівень вікна - aі outerфункції знаходяться на найвищому рівні в ланцюзі області.
  2. коли зовнішня функція називається новою variable scope object(і включається в ланцюг сфери застосування), що додається зі змінною bвсередині неї.

Тепер, коли потрібна змінна, aвона спочатку шукає найближчу область змінної області, а якщо змінної немає, ніж вона переходить до наступного об'єкта ланцюга змінної області дії. Що в цьому випадку - рівень вікна.


1
Не впевнений, чому це не прийнята відповідь. Насправді існує просто функціональний обсяг (до ECMA6 не було "локальної сфери") та глобальні прив'язки
texasbruce

9

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

Отже, з цієї точки зору, я думаю, що картина допомогла б мені знайти книгу про сфери та закриття Кайла Сімпсона:

зображення

Цитуючи його електронну книгу:

Будівля представляє вкладений набір правил нашої програми. Перший поверх будівлі представляє ваші сфери виконання, де б ви не знаходилися. Верхній рівень будівлі - це глобальна сфера. Ви вирішуєте посилання на LHS та RHS, дивлячись на свій поточний поверх, а якщо не знайдете, піднявши ліфт на наступний поверх, заглянувши туди, то на наступний тощо. Як тільки ви дістанетесь до останнього поверху (глобальна сфера), ви або знайдете те, що шукаєте, або не зробите. Але ви повинні зупинитись незалежно.

Варто зазначити одне, що варто відзначити: "Перегляд області припиняється, коли він знайде першу відповідність".

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


8

запустити код. сподіваємось, що це дасть уявлення про масштаби

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);

8

Глобальний обсяг:

Глобальні змінні точно схожі на світові зірки (Джекі Чан, Нельсон Мандела). Ви можете отримати доступ до них (отримати або встановити значення) з будь-якої частини програми. Глобальні функції схожі на глобальні події (Новий рік, Різдво). Ви можете їх виконати (зателефонувати) з будь-якої частини вашої програми.

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Місцевий обсяг:

Якщо ви перебуваєте в США, ви, можливо, знаєте Кім Кардашян, сумно відому знаменитість (їй якось вдається скласти таблоїди). Але люди за межами США її не впізнають. Вона - місцева зірка, прив’язана до своєї території.

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

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

Перевірте цю статтю, щоб глибше зрозуміти сферу застосування


6

Є лише два типи областей JavaScript:

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

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

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Використання функцій:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

У першому прикладі не було блокової області, тому спочатку оголошені змінні були перезаписані. У другому прикладі з'явилася нова область застосування завдяки функції, тому спочатку оголошені змінні були ЗАМЕНЕНІ, а не перезаписані.

Це майже все, що вам потрібно знати в частині розміщення JavaScript, за винятком:

  • Спробуйте / catch ввести нову область ТІЛЬКИ для самої змінної винятку, інші змінні не мають нової області
  • Мабуть, з-пропозицією є ще одним винятком, але використання з-пункту дуже сильно не рекомендує ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with )

Таким чином, ви можете бачити, що масштабування JavaScript насправді надзвичайно просте, хоч і не завжди інтуїтивно зрозуміле. Деякі речі, про які слід пам’ятати:

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

Отже цей код:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

еквівалентно:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Це може здатися протилежним інтуїтивно зрозумілим, але це має сенс з точки зору імперативного дизайнера мови.


5

Сучасні Js, ES6 +, ' const' і ' let'

Ви повинні використовувати блок-аналіз для кожної створеної змінної, як і більшість інших основних мов. varє застарілим . Це робить ваш код більш безпечним та ретельним.

constслід використовувати в 95% випадків . Це робить його таким чином, що посилання на змінну не може змінитися. Властивості масиву, об'єкта та DOM можуть змінюватися і, швидше за все, бути const.

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

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


3

Спробуйте цей цікавий приклад. У наведеному нижче прикладі, якщо число було ініціалізовано на 0, ви побачите 0, а потім 1. За винятком a - це об'єкт, і javascript передасть f1 вказівник, а не його копію. У результаті ви отримуєте одне і те ж попередження обох разів.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());

3

У JS є лише сфери функціонування. Не блокуйте сфери застосування! Ви також можете бачити те, що піднімається.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);

(давно з часу публікації відповіді) Область блоку; developer.mozilla.org/uk/docs/Web/JavaScript/Reference/…
Боб

2

Я розумію, що існує 3 сфери: глобальна сфера дії, доступна в усьому світі; локальна область, доступна для всієї функції незалежно від блоків; і область блоку, доступна лише для блоку, висловлювання або виразу, для якого воно було використано. Глобальна та локальна сфера дії вказуються на ключове слово 'var', або в межах функції, або поза її межами, а область блоку вказується на ключове слово 'let'.

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let


2

Дуже поширена проблема, ще не описана тим, що часто зустрічаються фронтальні кодери - це область, видима для обробника подій вбудованого тексту в HTML - наприклад, з

<button onclick="foo()"></button>

Обсяг змінних, на on*які може посилатися атрибут, повинен бути:

  • глобальний (робочі обробники вбудованих даних майже завжди посилаються на глобальні змінні)
  • властивість документа (наприклад, querySelectorяк окрема змінна вкаже на document.querySelector; рідко)
  • властивість елемента, до якого додається обробник (як вище; рідко)

В іншому випадку ви отримаєте ReferenceError, коли викликається обробник. Так, наприклад, якщо обробник вбудованого посилання посилається на функцію, яка визначена всередині, window.onload або $(function() {посилання вийде з ладу, тому що вбудований обробник може посилатися лише на змінні в глобальній області, а функція не є глобальною:

Властивості documentта властивості елемента, до якого додається обробник, також можуть посилатися як окремі змінні всередині вбудованих обробників, оскільки вбудовані обробники викликаються всередині двох withблоків , одного для documentелемента, одного для елемента. Ланцюг застосування змінних всередині цих обробників є надзвичайно неінтуїтивним , і обробник робочих подій, ймовірно, потребує функції, яка має бути глобальною (а також слід уникати зайвого глобального забруднення ).

Оскільки ланцюг сфери дії всередині вбудованих обробників настільки дивний , а оскільки вбудовані обробники вимагають глобального забруднення для роботи, а оскільки вбудовані обробники іноді вимагають уникнути потворних рядків при передачі аргументів, їх, мабуть, простіше уникнути. Натомість, прикріплюйте обробники подій за допомогою Javascript (наприклад, з addEventListener), а не з розміткою HTML.


З іншого боку, на відміну від звичайних <script>тегів, які працюють на найвищому рівні, код всередині модулів ES6 працює у власному приватному масштабі. Змінна, визначена у верхній частині звичайного <script>тегу, є глобальною, тому ви можете посилатися на неї в інших <script>тегах, таких як:

Але верхній рівень модуля ES6 не є глобальним. Змінна, оголошена у верхній частині модуля ES6, буде видима лише всередині цього модуля, крім випадків, коли змінна явно exportредагується, або якщо вона не присвоєна властивості глобального об'єкта.

Верхній рівень модуля ES6 аналогічний рівню внутрішньої частини IIFE на верхньому рівні в нормі <script>. Модуль може посилатися на будь-які змінні, які є глобальними, і нічого не може посилатися ні на що всередині модуля, якщо модуль явно не призначений для цього.


1

У JavaScript є два типи сфери застосування:

  • Місцевий обсяг
  • Глобальна сфера застосування

Функція "Далі" має змінну локальної області carName. І ця змінна недоступна за межами функції.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

Клас "Нижній" має змінну глобальної області застосування carName. І ця змінна доступна звідусіль у класі.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}

1

ES5 і раніше:

Змінні в Javascript спочатку (попередньо ES6) лексично функціонували. Термін, що визначається лексично, означає, що ви можете побачити область змінних, "переглянувши" код.

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

  1. Коли функція шукає значення змінної, вона спочатку розглядає власну область. Це тіло функції, тобто все між фігурними дужками {} (крім змінних усередині інших функцій, що знаходяться в цій області).
  2. Якщо він не може знайти змінну всередині функції функції, він підніметься до ланцюга і перегляне область змінної у функції, де визначена функція . Це те, що мається на увазі під лексичним розмахом, ми можемо бачити в коді, де ця функція була визначена, і, таким чином, ми можемо визначити ланцюг області, просто подивившись на код.

Приклад:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

Що відбувається , коли ми намагаємося увійти змінні foo, barі foobarв консолі наступне:

  1. Ми намагаємося записати foo на консоль, foo можна знайти всередині самої функції innerFunc. Тому значення foo вирішено на рядок innerFunc.
  2. Ми намагаємося увійти до консолі, бар не можна знайти всередині самої функції innerFunc. Тому нам потрібно піднятися по ланцюгу розмаху . Спочатку ми розглянемо зовнішню функцію, в якій innerFuncбула визначена функція . Це функція outerFunc. В області outerFuncми можемо знайти смугу змінної, яка містить рядок 'externalFunc'.
  3. foobar не можна знайти у innerFunc. . Таким чином, нам потрібно підняти ланцюг розмаху до області InternalFunc. Тут також його неможливо знайти, ми піднімаємось на інший рівень до глобальної сфери (тобто найбільш віддаленої області). Тут ми знаходимо змінну foobar, яка містить рядок 'global'. Якщо вона не знайшла б змінну після підйому по ланцюгу області, двигун JS видав би referenceError .

ES6 (ES 2015) та старші:

Ті самі поняття лексично сфери та сфери застосування все ще застосовуються в ES6. Однак були введені нові способи декларування змінних. Є такі:

  • let: створює блок-змінну
  • const: створює змінну в масштабі блоку, яку необхідно ініціалізувати і не може бути переназначена

Найбільша відмінність між varта let/ constє в тому, що varце функція, в той час як let/ constє блоком. Ось приклад для ілюстрації цього:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

У наведеному вище прикладі letVar записує значення global, тому що змінні, оголошені з, letє блок-шкалою. Вони перестають існувати за межами відповідного блоку, тому змінна не може бути доступна поза блоком if.


0

У EcmaScript5 в основному є дві сфери, локальна сфера та глобальна сфера, але в EcmaScript6 ми маємо в основному три сфери, локальну область, глобальну область застосування та нову область, що називається блок-область .

Приклад області блоку:

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}

0

ECMAScript 6 представив ключові слова let and const. Ці ключові слова можна використовувати замість ключового слова var. На відміну від ключового слова var, ключові слова let та const підтримують декларацію локальної області дії в операторах блоку.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10

0

Мені дуже подобається прийнята відповідь, але я хочу додати це:

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

Область застосування - це набір правил пошуку змінних за назвою їх ідентифікатора.

  • Якщо змінну неможливо знайти в безпосередній області застосування, Engine звертається до наступної зовнішньої області застосування, продовжуючи до тих пір, поки не буде знайдено або поки не буде досягнуто найбільш зовнішньої (aka, глобальної) області.
  • Це набір правил, який визначає, де і як можна шукати змінну (ідентифікатор). Цей огляд може бути призначений для присвоєння змінній, яка є посиланням на LHS (зліва), або може бути для отримання її значення, яке є посиланням RHS (праворуч) .
  • LHS-посилання є результатом операцій по призначенню. Присвоєння, пов’язані зі сферою дії, можуть відбуватися або з оператором =, або шляхом передачі аргументів до параметрів функції (призначити).
  • Двигун JavaScript спочатку компілює код перед його виконанням, і таким чином він розбиває оператори типу var a = 2; у два окремі етапи: 1-й. По-перше, var a, щоб оголосити це в цій області. Це виконується на початку, перед виконанням коду. 2-й. Пізніше a = 2 шукати змінну (посилання LHS) і призначити її, якщо вона знайдена.
  • Як LHS, так і RHS довідкові погляди починаються з поточної області виконання, і якщо це потрібно (тобто вони не знаходять, що там шукають), вони просуваються до вкладеної області, однієї області (підлога ), шукаючи ідентифікатор, доки вони не дістаються до глобального (верхній поверх) і не зупиняються, і або знаходять його, або не роблять. Невиконані посилання на RHS призводять до відкидання ReferenceError. Невиконані посилання на LHS призводять до автоматичного, неявно створеного глобального цього імені (якщо він не в строгому режимі) або ReferenceError (якщо в строгому режимі).
  • Об'єм складається з серії "бульбашок", які кожен виступають як контейнер або відро, в яких оголошуються ідентифікатори (змінні, функції). Ці бульбашки акуратно гніздяться всередині один одного, і це гніздування визначено в час автора.

-3

У JavaScript існує два типи областей.

  1. Глобальний діапазон : змінна, яка оголошена в глобальному масштабі, може бути використана в будь-якому місці програми дуже гладко. Наприклад:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. Функціональна область або Локальна область : змінна, оголошена в цій області, може використовуватися лише в її власній функції. Наприклад:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.