網站搜尋

javascript - 為什麼有些在函數內使用 let 宣告的變數會在另一個函數中可用,而其他的則會造成引用錯誤?


我不明白為什麼在函數內宣告的變數會表現得如此奇怪。

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

答案#1

那是因為你其實在說:

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

而不是你認為自己正在說的,那就是:

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

你會注意到,無論你在鏈中添加多少變數,只有第一個 (a) 會引起錯誤。

這是因為「let」將你的變數作用域限制在你宣告它的區塊(或者,『局部地』,大致上意思是『在大括號內』)中。

如果你在沒有使用「let」的情況下宣告變數,它會將變數作用域設為全域。

所以,在你設定變數的函數中,所有變數都會被賦值為 10(如果你在除錯器中設置斷點,你可以看到這一點)。如果你在那個第一個函數中對 a、b、c 設置 console log,一切都正常。

但一旦你離開那個函數,第一個 (a)--再提醒一次,從賦值的順序上來看,它其實是最後一個--就會「消失」(同樣,如果你在第二個函數設置斷點,你可以在除錯器中看到這一點),但其他兩個(或者你添加的任何其他)仍然可用。

這是因為,“let”只適用於鏈中的第一個變數(所以只在本地範圍內作用)——再次強調,從技術上講,它是最後被聲明和賦值的變數。其餘的技術上前面沒有“let”,所以從技術上講它們是全局聲明的(也就是在全局對象上),這就是為什麼它們會出現在你的第二個函數中。

試試看:移除「let」關鍵字。你所有的變數現在都可以使用了。

"var" 有類似的區域作用域效果,但在變數如何被「提升」上有所不同,這是你絕對應該理解的,但與你的問題沒有直接關係。

(順便說一句,這個問題會難倒足夠多的專業 JavaScript 開發者,使它成為一個好問題)。

強烈建議你花時間了解在 JS 中變數可以如何宣告的差異:沒有使用關鍵字、使用「let」以及使用「var」。

答案#2

在函數 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();

答案#3

在稱某些事物為奇怪之前,先讓我們了解一些基本知識:

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
}