Чому деякі змінні, оголошені за допомогою пускають всередину функції, стають доступними в іншій функції, а інші призводять до помилки посилання?


158

Я не можу зрозуміти, чому змінні діють так дивно, коли оголошуються всередині функції.

  1. У firstфункції я оголошую зі letзмінними bта cзі значенням 10 :

    b = c = 10;

    У secondфункції я показую:

    b + ", " + c

    І це показує:

    10, 10
  2. Також у firstфункції я оголошую aзі значенням 10 :

    let a = b = c = 10;

    Але у secondфункції він показує помилку:

    Неможливо знайти змінну: a

  3. Тепер у firstфункції я оголошую dзі значенням 20 :

    var d = 20;

    Але у secondфункції він показує ту саму помилку, що і раніше, але зі змінною d:

    Неможливо знайти змінну: d

Приклад:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()


31
Ви оголошуючи глобальний, так bі cНЕ передує varключовим словом. aі dє місцевими для first.
ВЛАЗ

1
Коментарі не для розширеного обговорення; дотична розмова про те, чи це було б добре питання інтерв'ю, було заархівовано у чаті .
Коді Грей

1
Це нагадує мені подібну ситуацію у Visual Basic; Dim Apple, Banana, Pear As Fruitозначає Dim Apple / Dim Banana / Dim Pear As Fruit, а не Dim Apple As Fruit / ....
Ерік Ліпперт

Відповіді:


179

Це тому, що ти насправді кажеш:

c = 10;
b = c;
let a = b;

І не те, що ви думаєте, що говорите, а саме:

let a = 10;
let b = 10;
let c = 10;

Ви помітите, що незалежно від того, скільки змінних ви додасте до ланцюжка, помилка буде лише першою (а).

Це тому, що "нехай" прив'язує вашу змінну до блоку (або, "локально", більш-менш значення "в дужках"), в якому ви заявляєте про це.

Якщо ви оголошуєте змінну без "нехай", вона змінює масштаб змінної в усьому світі.

Отже, у функції, де ви встановлюєте свої змінні, все отримує значення 10 (це ви можете побачити на відладчику, якщо поставити точку розриву). Якщо ви помістите журнал консолі для a, b, c у цій першій функції, все добре.

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

Це тому, що "нехай" ПРИЄДНАЄТЬСЯ ТОЛЬКО (так лише на локальних обсягах) ПЕРШИЙ ЗМІНО - знову ж таки, що технічно є останнім задекларованим та присвоєним значенням - у ланцюжку. Решту технічно не "пускають" перед собою. Таким чином, вони технічно оголошені в усьому світі (тобто на глобальному об'єкті), тому вони з'являються у вашій другій функції.

Спробуйте: видаліть ключове слово "нехай". Усі ваші варіанти тепер будуть доступні.

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

(До речі, це запитання натхне достатньо професійних розробників JS, щоб зробити його гарним).

Наполегливо пропоную вам витратити час на відмінності в тому, як змінні можна оголосити в JS: без ключового слова, з "нехай" та з "var".


4
Це одночасно найкраще і найгірше, що стосується програмування: комп’ютер буде робити саме те , що ти йому кажеш. Не обов’язково те, що ви мали намір сказати. Програми ідеальні. Ми створюємо проблеми.
Niet the Dark Absol

8
@Thevs Чому ви рекомендуєте varбільш letв контексті цієї відповіді? Я не розумію.
Клайкон

4
@Те я категорично не згоден з вами. varможуть бути схильні до помилок, якщо використовувати їх необережно. Перевірте цю скрипку
Cid

2
@Те в якому випадку varмає якусь перевагу перед let? Дозвольте уточнити: сучасний контекст, коли обидва - це варіанти, і я прошу коду, який слід написати. Коли я вже запитував це раніше, я отримав відповіді про "ви можете повторно оголосити змінну за допомогою var", і в такому випадку я повинен нагадати людям, що ви не повинні повторно декларувати змінні . Це або помилка, або помилка в логіці коду - тому перевага декларування полягає в тому, що вона дозволяє писати несправний код. Я ще не бачив жодної розумної причини на користь, varколи letце також є варіантом.
ВЛАЗ

2
Помістіть ще одну позначку проти JavaScript. Усі змінні є глобальними, якщо не оголошені локальними. :(
JRE

68

У функції first()змінні bі cстворюються на льоту, без використання varабо let.

let a = b = c = 10; // b and c are created on the fly

Інакше, ніж

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

Вони стають неявними глобальними. Ось чому вони доступні вsecond()

З документації

Присвоєння значення незадекларованій змінній неявно створює її як глобальну змінну (вона стає властивістю глобального об'єкта) при виконанні призначення.

Щоб цього уникнути, ви можете використати "use strict"помилки, коли використовуватиметься недекларована змінна

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


15
Крім того: let a = 10, b = 10, c = 10;або let a, b, c; a = b = c = 10;інакше був би правильний спосіб декларування змінних.
Рікард Елімяа

Тож із суворим режимом, як щодо змінної b?
Tick20

2
@ Tick20 змінна bне буде оцінена / досягнута, помилка буде виникати на рядку let a = b = c = 10;, прочитаному справа наліво . cбудучи першою змінною, що викликає ReferenceError, решта рядка не буде виконана (сценарій припинився)
Cid

2
щось подібне let a = 10, b = a, c = b;діє і теж
Каддат

8
прихильне в основному за "суворість використання". У контексті запитання про інтерв'ю це також буде початком мого коментаря до цього коду.
Pac0

23

Перш ніж називати речі дивними, спочатку ознайомимося з основами:

var та let використовуються для оголошення змінної в JavaScript. Наприклад,

var one = 1;
let two = 2;

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

three = 3;

Тепер різниця між перерахованими вище підходами полягає в тому, що:

var функція, що охоплюється

і

let блок-сфера.

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

Доступ до глобальних змінних можна отримати з будь-якої точки веб-сторінки (не рекомендується, оскільки глобальні дані можуть бути випадково змінені).

Тепер, згідно з цими поняттями, давайте подивимось на розглянутий код:

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}

1
Ви заплатили частину відповіді JonoJames . Чому?
Пітер Мортенсен

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

2
Можливо, обидві відповіді містять вміст, скопійований з одного оригінального джерела без очевидного цитування - можливо, tutorialsteacher.com/javascript/javascript-variable . Наявність плагіату очевидна, оскільки граматична помилка відтворена з оригіналу - "область змінних, оголошених без varключового слова, стає глобальною незалежно від місця декларування", повинна бути або "сфера ... стає", або " сфери ... стають " . Використання чужих точних слів вимагає цитування, чи то звідси, чи деінде. meta.stackexchange.com/q/160071/211183
Michael - sqlbot

Дякую, хлопці, я додав посилання на джерело.
fatimasajjad

6

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

Кожна змінна, яку ви заявляєте таким чином, не використовує letабо var. У декларації змінних вам не вистачає кома.

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

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


3

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

let a = 10;
let b = 10;
let c = 10;

2

Дивна проблема викликана правилами розміщення в JavaScript

function first() {
   let a = b = c = 10; // a is in local scope, b and c are in global scope
   var d = 20; // d is in local scope
   second(); // will have access to b and c from the global scope
}

Якщо припустити, що потрібно оголосити 3 локальні змінні, ініціалізовані на одне значення (100). Ваш перший () буде виглядати як нижче. У цьому випадку second () не матиме доступу до жодної зі змінних, оскільки вони локальні для first ()

function first() {
   let a = 100; // a is in local scope init to 100
   let b = a; // b is in local scope init to a
   let c = b // c is in local scope init to b

   var d = 20; // d is in local scope
   second(); // will not have access a, b, c, or d
}

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

function first() {
   a = 100; // a is in global scope
   b = a; // b is in global scope
   c = b // c is in global scope

   d = 20; // d is in global scope
   second(); // will have access to a, b, c, and d from the global scope
}

Локальні змінні (ака. Доступні в кодовому блоці, де вони оголошені).
Блок коду - це будь-який {}, у якому рядки між кодом.

  • function () {var, let, const in тут доступний всій функції},
  • for () {var in тут доступний для зовнішньої області, нехай, const доступний лише тут},
  • тощо.

Глобальні змінні (також доступні в глобальній області).
Ці змінні приєднані до глобального об'єкта. Глобальний об’єкт залежить від середовища. Це об’єкт вікна в браузерах.

Спеціальна примітка: Ви можете оголосити змінні в JavaScript, не використовуючи ключові слова var, let, const. Змінна, оголошена таким чином, приєднується до глобального об'єкта, тому доступна у глобальному масштабі.
a = 100 // is valid and is in global scope

Деякі статті для подальшого читання: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in-javascript https: //www.digitalocean .com / спільнота / підручники / розуміння-змінні-область-підйом-в-JavaScript


0

Основна відмінність - це правила розміщення. Змінні, оголошені за ключовим словом var, передаються в безпосереднє тіло функції (отже, область функцій), тоді як дозволені змінні приведені до прямого блоку, що вкладається, позначається {} (звідси область блоку). І коли ти кажеш

c = 10;
b = c;
let a = b;

c і b мають тривалість життя, як весело, але мають лише блоковий проміжок, і якщо ви намагаєтеся отримати доступ до a, посилаючись на нього, завжди відображається помилка, але c і b є в усьому світі, тому вони не роблять. Ви помітите, що незалежно від кількості змінних, які ви додаєте до ланцюжка, це буде лише перша (а), яка викликає помилку. Це тому, що "нехай" привласнює вашу змінну до блоку (або, "локально", більш-менш означає "у дужках") у якому ви декларуєте це. Якщо ви оголошуєте змінну без "нехай", вона поширюється на змінну глобально. Отже, у функції, де ви встановлюєте свої змінні, все отримує значення 10 (це ви можете побачити у відладчику, якщо поставити точка зламу). Якщо ви помістите журнал консолі для a, b, c у цій першій функції, все добре. Але як тільки ви залишите цю функцію, перший (a) - і знову, майте на увазі,


0

Ось 3 цікаві аспекти змінних оголошень у JavaScript:

  1. var обмежує область змінної блоком, в якому вона визначена. ( "var" призначений для локального застосування .)

  2. нехай дозволяє тимчасове переосмислення значення зовнішньої змінної всередині блоку.

  3. Просто оголошення змінної без var або let зробить змінну глобальною, незалежно від того, де вона оголошена.

Ось демонстрація let , яка є останнім доповненням до мови:

// File name:  let_demo.js

function first() {
   a = b = 10
   console.log("First function:    a = " + a)
   console.log("First function:    a + b = " + (a + b))
}

function second() {
    let a = 5
    console.log("Second function:    a = " + a)
    console.log("Second function:    a + b = " + (a + b))
}

first()   

second()

console.log("Global:    a = " + a)
console.log("Global:    a + b = " + (a + b))

Вихід:

$ node let_demo.js 

First function:    a = 10
First function:    a + b = 20

Second function:    a = 5
Second function:    a + b = 15

Global:    a = 10
Global:    a + b = 20

Пояснення:

Змінні a і b були відмінені кольорами всередині ' first () ', без var або let ключових слів.

Отже, a і b є глобальними, а отже, доступними у всій програмі.

У функції з назвою "second" вираз "let a = 5" тимчасово встановлює значення " a " на " 5 ", лише в межах функції.

Поза межами області ' second () ', IE, у глобальному масштабі значення ' a ' буде таким, як було визначено раніше.

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