日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

在變量中存儲值是編程中的一個基本概念。變量的“范圍”決定了它在整個程序中何時可用和不可用。理解 JAVAScript 中的變量作用域是在語言中打下堅實基礎的關鍵之一。

本文將解釋 JavaScript 的作用域系統是如何工作的。您將了解聲明變量的不同方式、局部作用域和全局作用域之間的區別,以及稱為“提升”的東西——一種 JavaScript 怪癖,可以將看似無辜的變量聲明變成一個微妙的錯誤。

變量范圍

在 JavaScript 中,變量的范圍由變量聲明的位置控制,它定義了可以訪問特定變量的程序部分。

目前,在 JavaScript 中聲明變量的方法有三種:使用 oldvar關鍵字,以及使用 newlet和const關鍵字。在 ES6 之前,使用var關鍵字是聲明變量的唯一方法,但現在我們可以使用letand const,它有更嚴格的規則,并且代碼更不容易出錯。我們將在下面探討所有三個關鍵字之間的差異。

范圍規則因人而異。JavaScript 有兩個作用域:globallocal。本地作用域有兩種變體:舊的函數作用域和ES6 引入的新塊作用域。值得注意的是,函數作用域實際上是塊作用域的一種特殊類型。

全球范圍

在腳本中,最外層的作用域是全局作用域。在此范圍內聲明的任何變量都將成為全局變量,并且可以從程序中的任何位置訪問:

// Global Scope

const name = "Monique";

function sayHi() {
  console.log(`Hi ${name}`);
}

sayHi();
// Hi Monique

正如這個簡單的示例所示,該變量name是全局變量。它是在全局范圍內定義的,并且可以在整個程序中訪問。

但是,盡管這看起來很方便,但在 JavaScript 中不鼓勵使用全局變量。例如,這是因為它們可能會被其他腳本或程序中的其他地方覆蓋。

本地范圍

在塊內聲明的任何變量都屬于該特定塊并成為局部變量。

varJavaScript 中的函數定義了使用,let和聲明的變量的范圍const。在該函數中聲明的任何變量只能從該函數和任何嵌套函數中訪問。

代碼塊(if,for等)僅為使用letandconst關鍵字聲明的變量定義范圍。該var關鍵字僅限于函數作用域,這意味著只能在函數內部創建新作用域。

letandconst關鍵字具有塊作用域,它為聲明它們的任何塊創建一個新的本地作用域。您還可以在 JavaScript 重新定義獨立的代碼塊,它們類似地劃定一個范圍:

{
  // standalone block scope
}

函數和塊作用域可以嵌套。在這種情況下,使用多個嵌套范圍,可以在其自己的范圍內或從內部范圍訪問變量。但在其范圍之外,該變量是不可訪問的。

幫助可視化范圍的簡單示例

為了清楚起見,讓我們使用一個簡單的比喻。我們世界上的每個國家都有邊界。這些邊界內的一切都屬于國家的范圍。每個國家都有很多城市,每個城市都有自己的城市范圍。國家和城市就像 JavaScript 函數或塊。他們有自己的本地范圍。各大洲也是如此。盡管它們的大小很大,但它們也可以定義為語言環境。

另一方面,世界海洋不能被定義為具有局部范圍,因為它們實際上包裹了所有局部對象——大陸、國家和城市——因此,它們的范圍被定義為全球。讓我們在下一個示例中對此進行可視化:

var locales = {
  europe: function() {          // The Europe continent's local scope
    var myFriend = "Monique";

    var france = function() {   // France country's local scope
      var paris = function() {  // The Paris city's local scope
        console.log(myFriend);  // output: Monique
      };

      paris();
    };

    france();
  }
};

locales.europe();

在這里,myFriend變量可以從paris函數中獲得,因為它是在france函數的外部作用域中定義的。如果我們交換myFriend變量和控制臺語句,我們會得到ReferenceError: myFriend is not defined,因為我們無法從外部作用域到達內部作用域。

現在我們了解了本地和全局范圍是什么以及它們是如何創建的,現在開始學習 JavaScript 解釋器如何使用它們來查找特定變量的時候了。

回到給定的比喻,假設我想找到一個名叫 Monique 的朋友。我知道她住在巴黎,所以我從那里開始尋找。當我在巴黎找不到她時,我會上一層樓,在整個法國擴大我的搜索范圍。但又一次,她不在那里。接下來,我通過更上一層樓再次擴大我的搜索范圍。最后,我在意大利找到了她,在我們的案例中,意大利是歐洲的本地范圍。

在前面的示例中,我的朋友 Monique 有變量 表示myFriend。在最后一行我們調用europe()函數,它調用france(),最后當paris()函數被調用時,搜索開始。JavaScript 解釋器從當前執行的作用域開始工作,直到找到有問題的變量為止。如果在任何范圍內都找不到該變量,則會引發異常。

這種類型的查找稱為詞法(靜態)范圍。程序的靜態結構決定了變量范圍。變量的范圍由其在源代碼中的位置定義,嵌套函數可以訪問在其外部范圍中聲明的變量。無論從哪里調用函數,甚至如何調用它,它的詞法范圍都只取決于函數的聲明位置。

現在讓我們看看新的塊作用域是如何工作的:

function testScope(n) {
  if (true) {
    const greeting = 'Hello';
    let name = n;
    console.log(greeting + " " + name); // output: Hello [name]
  }
  console.log(greeting + " " + name); // output: ReferenceError: greeting is not defined
}

testScope('David');   

在這個例子中,我們可以看到用 and 聲明的andgreeting變量name在塊外是不可訪問的。constletif

現在讓我們替換andconst看看會發生什么:letvar

function testScope(n) {
  if (true) {
    var greeting = 'Hello';
    var name = n;
    console.log(greeting + " " + name); // output: Hello [name]
  }
  console.log(greeting + " " + name); // output: Hello [name]
}

testScope('David');

如您所見,當我們使用var關鍵字時,變量在整個函數范圍內都是可訪問的。

在 JavaScript 中,可以在多層嵌套范圍內指定同名變量。在這種情況下,局部變量優先于全局變量。如果你聲明了一個同名的局部變量和一個全局變量,當你在函數或塊中使用它時,局部變量將優先。這種類型的行為稱為遮蔽。簡單地說,內部變量遮蔽了外部變量。

這就是 JavaScript 解釋器試圖查找特定變量時使用的確切機制。它從當時正在執行的最內層范圍開始,一直持續到找到第一個匹配項,無論外層級別中是否存在其他同名變量。讓我們看一個例子:

var test = "I'm global";

function testScope() {
  var test = "I'm local";

  console.log (test);     
}

testScope();           // output: I'm local

console.log(test);     // output: I'm global

即使名稱相同,局部變量在函數執行后也不會覆蓋全局變量testScope()。但情況并非總是如此。讓我們考慮一下:

var test = "I'm global";

function testScope() {
  test = "I'm local";

  console.log(test);     
}

console.log(test);     // output: I'm global

testScope();           // output: I'm local

console.log(test);     // output: I'm local (the global variable is reassigned)

這一次,局部變量test覆蓋了同名的全局變量。當我們在testScope()函數內部運行代碼時,全局變量被重新分配。如果一個局部變量在沒有首先用關鍵字聲明的情況下被賦值var,它就變成了一個全局變量。為避免此類不良行為,您應始終在使用局部變量之前聲明它們。在函數中使用關鍵字聲明的任何變量var都是局部變量。聲明變量被認為是最佳實踐。

注意:在嚴格模式下,如果沒有先聲明變量就給變量賦值是錯誤的。

吊裝

JavaScript 解釋器在幕后執行許多操作,其中之一就是“提升”。如果您不知道這種“隱藏”行為,可能會引起很多混亂。考慮 JavaScript 變量行為的最佳方式是始終將它們可視化為由兩部分組成:聲明和初始化/賦值:

var state;             // variable declaration
state = "ready";       // variable assignment

var state = "ready";   // declaration plus assignment

在上面的代碼中,我們首先聲明了變量state,然后我們給它賦值"ready"。而在最后一行代碼中,我們看到這兩個步驟可以合并。但是您需要記住的是,即使它們看起來像一個語句,實際上 JavaScript 引擎也會將該單個語句視為兩個單獨的語句,就像示例的前兩行一樣。

我們已經知道在一個范圍內聲明的任何變量都屬于該范圍。但我們還不知道的是,無論變量在特定范圍內聲明的位置,所有變量聲明都會移動到其范圍的頂部(全局或局部)。這稱為提升,因為變量聲明被提升到范圍的頂部。請注意,提升只會移動聲明。任何分配都留在原地。讓我們看一個例子:

console.log(state);   // output: undefined
var state = "ready";

如您所見,當我們記錄 的值時state,輸出為undefined,因為我們在實際賦值之前引用了它。您可能期望 aReferenceError被拋出,因為state尚未聲明。但你不知道的是,變量undefined在幕后用默認值聲明和初始化的。以下是 JavaScript 引擎解釋代碼的方式:

var state;           // moved to the top
console.log(state);   
state = "ready";     // left in place

重要的是要注意變量沒有物理移動。吊裝只是一個模型,描述了 JS 引擎在幕后所做的事情。

現在,讓我們看看提升是如何與let變量一起工作的:

{
  // Temporal dead one (TDZ) starts at the beginning of the scope
  console.log(state);   // output: "ReferenceError: Cannot access 'state' before initialization
  let state = "ready";  // end of TDZ. TDZ ends at actual variable declaration
}   

在此示例中,控制臺輸出不是undefined,但會引發引用錯誤。為什么?let與變量相比,var變量在完全初始化之前無法讀取/寫入。它們僅在代碼中實際聲明的地方才被完全初始化。因此,let變量聲明被提升但未使用undefined值初始化,變量就是這種情況var。從塊開始到實際變量聲明的部分稱為Temporal Dead Zone。這是一種確保更好的編碼實踐的機制,強制您在使用變量之前聲明它。如果我們將控制臺語句移出 TDZ,我們將得到預期的輸出:ready.

{
  // Temporal dead one (TDZ) starts at the beginning of the scope
  let state = "ready";  // end of TDZ. TDZ ends at actual variable declaration
  console.log(state);   // output: ready
} 

用關鍵字聲明的變量與變量const具有相同的行為let。

職能

提升也會影響函數聲明。但在我們看一些例子之前,讓我們先了解一下函數聲明和函數表達式之間的區別:

function showState() {}          // function declaration
var showState = function() {};   // function expression

區分函數聲明和函數表達式的最簡單方法是檢查單詞function在語句中的位置。如果function是語句中的第一件事,那么它就是一個函數聲明。否則,它是一個函數表達式。

函數聲明被完全提升。這意味著整個函數的主體被移動到頂部。這允許您在聲明函數之前調用它:

showState();            // output: Ready

function showState() {
  console.log("Ready");
} 

var showState = function() {
  console.log("Idle");
};

上述代碼有效的原因是 JavaScript 引擎將showState()函數的聲明及其所有內容移到了作用域的開頭。代碼解釋如下:

function showState() {     // moved to the top (function declaration)
  console.log("Ready");
} 

var showState;            // moved to the top (variable declaration)

showState();  

showState = function() {   // left in place (variable assignment)
  console.log("Idle");
};

您可能已經注意到,只有函數聲明被提升,但函數表達式沒有。將函數分配給變量時,規則與變量提升的規則相同(僅移動聲明,而分配保留在原地)。

在上面的代碼中,我們看到函數聲明優先于變量聲明。在下一個示例中,我們將看到,當我們有一個函數聲明與一個變量賦值時,最后一個優先:

var showState = function() {
  console.log("Idle");
};

function showState() {
  console.log("Ready");
} 

showState();            // output: Idle

這一次,我們showState()在代碼的最后一行調用了函數,這改變了情況。現在我們得到輸出"Idle"。下面是它在被 JavaScript 引擎解釋時的樣子:

function showState(){        // moved to the top (function declaration)
  console.log("Ready");
} 

var showState;               // moved to the top (variable declaration)

showState = function(){      // left in place (variable assignment)
  console.log("Idle");
};

showState();

注意:箭頭函數與函數表達式的工作方式相同。

課程

類聲明也以與用let語句聲明的變量類似的方式提升:

// Using the Person class before declaration
var user = new Person('David', 33); // output: ReferenceError: Cannot access 'Person' before initialization

// Class declaration
class Person {
  constructor(name, age) {
    this.name = name; 
    this.age = age;
  }
}

在這個例子中,我們可以看到Person在聲明之前使用類會產生類似于在let變量中的引用錯誤。為了解決這個問題,我們必須Person在聲明之后使用類:

// Class declaration
class Person {
  constructor(name, age) {
    this.name = name; 
    this.age = age;
  }
}

// Using the Person class after declaration
var user = new Person('David', 33);
console.log(user); 

也可以使用類表達式、 usingvar或let變量const聲明語句來創建類:

// Using the Person class
console.log(typeof Person);   // output: undefined

var user = new Person('David', 33); // output: TypeError: Person is not a constructor

// Class declaration using variable statement
var Person = class {
  constructor(name, age) {
    this.name = name; 
    this.age = age;
  }
};

在這個例子中,我們可以看到這個Person類被提升為一個函數表達式,但它不能被使用,因為它的值是undefined. 同樣,為了解決這個問題,我們必須Person在聲明之后使用類:

// Using the Person class
console.log(typeof Person); // output: undefined

// Class declaration using variable statement
var Person = class {
  constructor(name, age) {
    this.name = name; 
    this.age = age;
  }
};

// Using the Person class after declaration
var user = new Person('David', 33);
console.log(user);  

要記住的事情

  • var變量是函數范圍的。
  • let并且const變量是塊范圍的(這也包括函數)。
  • 在執行代碼的任何部分之前,所有聲明(類、函數和變量)都被提升到包含范圍的頂部。
  • 首先提升函數,然后提升變量。
  • 函數聲明優先于變量聲明,但不高于變量賦值。

如果本文對你有幫助,別忘記給我個3連問 ,點贊,轉發,評論,,咱們下期見。

分享到:
標簽:JavaScript
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定