欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript變量聲明的var、let、const詳解

 更新時(shí)間:2022年07月08日 10:03:28   作者:Hardy_  
JavaScript中的變量是松散類型的,可以保存任何類型數(shù)據(jù),變量只不過是一個(gè)名稱,下面這篇文章主要給大家介紹了關(guān)于JavaScript變量聲明的var、let、const的相關(guān)資料,需要的朋友可以參考下

前言

一個(gè)程序語言在運(yùn)行的過程中,變量的聲明在整個(gè)程序的生命周期中,是不斷在進(jìn)行的過程。任何程序的計(jì)算都會(huì)涉及至少一個(gè)變量,而計(jì)算的結(jié)果的則可能會(huì)涉及到另外的一個(gè)或者多個(gè)變量。變量在使用前是要聲明,變量聲明的過程在計(jì)算機(jī)的底層,牽涉到的是內(nèi)存空間和內(nèi)存地址的分配。當(dāng)然啦,有內(nèi)存空間的分配,就會(huì)有內(nèi)存空間的回收和再分配。可以看出,變量聲明是我們?cè)诮佑|一門程序語言中起始的也是很關(guān)鍵的一步。

而本篇文章想跟大家聊聊的,是關(guān)于JavaScript的變量聲明中的一些內(nèi)容。

內(nèi)容

JavaScript的變量聲明

雖然一門語言的語法發(fā)展,變量聲明的語法和方式通常是比較起始的。程序語言的設(shè)計(jì)者通常都會(huì)在語言的初始的版本盡量根據(jù)自己對(duì)語言的設(shè)計(jì)思想,設(shè)計(jì)好語言的變量聲明的語法和方式,以保證好程序語言在未來的很長的道路上有一個(gè)比較好的起始。但是,JavaScript卻沒有一個(gè)這么好的起始。

JavaScript的設(shè)計(jì)之初衷,就是一門為了解決簡(jiǎn)單的網(wǎng)頁互動(dòng)的腳本語言。它的設(shè)計(jì),只用了短短的10天時(shí)間。就像初生的嬰兒一樣,稚嫩而沒有太多的規(guī)劃和愿景。設(shè)計(jì)者做夢(mèng)都沒有想到,JavaScript將來可以在整個(gè)互聯(lián)網(wǎng)的發(fā)展中占有這么大的一個(gè)分量,不僅在瀏覽器端,而且在移動(dòng)端、APP端、服務(wù)端、桌面應(yīng)用上都扮演著重要的角色。

JavaScript的設(shè)計(jì)的雛形是稚嫩的,但是隨著這么語言逐漸的受到關(guān)注和使用,語言本身的設(shè)計(jì)和語法,也在不斷的發(fā)展著。而JavaScript的變量聲明方式也從野蠻的var的聲明方式,前行到了ECMAScript標(biāo)準(zhǔn)的letconst的聲明方式。

接下來,我們一起來討論它們。

var的變量聲明

var用于聲明一個(gè)函數(shù)范圍或者全局范圍的變量,并且可以為其初始化一個(gè)值。它是ECMAScript2015之前的變量聲明的唯一關(guān)鍵字,曾經(jīng)承載過一代前端開發(fā)者的青蔥歲月以及水深火熱。它具有如下的特性:

變量聲明在函數(shù)作用域中

與大多數(shù)的語言不同,作為過去的JavaScript的唯一變量聲明方式,var的所聲明的變量并不是聲明在塊級(jí)作用域中 ,而是聲明在最近的函數(shù)上下文中,也就是局部函數(shù)作用域。對(duì)于未在任何函數(shù)中聲明的變量,其聲明范圍則是在全局對(duì)象global之中。

/* === 全局作用域 start === */
/*
  變量globalVar聲明在任何函數(shù)之外,為全局作用域中的變量
  任何的函數(shù)和方法都能訪問到這個(gè)變量
*/
var globalVar = 'global var';

/* === f1函數(shù)作用域 start === */
function f1() {
  /*
    變量localVar1聲明在函數(shù)f1中,為f1的局部函數(shù)作用域中的變量
    只有代碼詞法分析中的f1函數(shù)的內(nèi)部函數(shù)能夠訪問到這個(gè)變量
    在這個(gè)作用域中可以訪問到全局作用域中的變量globalVar
  */
  var localVar1 = 'local var1';
  /* === f2函數(shù)作用域 start === */
  return function f2() {
    /*
      變量localVar2聲明在函數(shù)f2中,為f2的局部函數(shù)作用域中的變量,
      只有代碼詞法分析中的f2函數(shù)的內(nèi)部函數(shù)能夠訪問到這個(gè)變量,
      在這個(gè)作用域中可以訪問到全局作用域中變量globalVar以及外層f1函數(shù)作用域中的localVar1
    */
    var localVar2 = 'local var2';
    return localVar2;
  /* === f2函數(shù)作用域 end === */
  }
/* === f1函數(shù)作用域 end === */
}

f1();
/* === 全局作用域 end === */

上面的代碼中,globalVar作為定義在全局對(duì)象global中的一個(gè)全局變量,除了可以直接通過變量標(biāo)識(shí)符globalVar訪問到之外,也可以通過global.globalVar或者window.globalVar的對(duì)象屬性訪問方式來訪問到。

同時(shí)還能看到,上面的作用域定義構(gòu)建成了一條global -> f1 -> f2的函數(shù)作用域鏈, 函數(shù)執(zhí)行過程中的變量訪問會(huì)根據(jù)這條函數(shù)作用域鏈進(jìn)行規(guī)則查找和訪問。

變量重復(fù)聲明

var的變量聲明支持在同一作用域中進(jìn)行同一個(gè)變量的多次重復(fù)的聲明,多次聲明中只有首次的變量聲明會(huì)被執(zhí)行,其他聲明會(huì)因?yàn)楫?dāng)前的執(zhí)行上下文對(duì)象中已存在當(dāng)前變量而被忽略。但是,聲明變量同時(shí)如果對(duì)變量進(jìn)行了初始賦值,賦值操作依舊會(huì)被執(zhí)行。

/*
  變量的首次聲明在當(dāng)前執(zhí)行上下文對(duì)象中新增一個(gè)value變量
*/
var value = 1;

/*
  除了變量的首次聲明外,其他相同的聲明,都不會(huì)被執(zhí)行,
  但是,聲明中的初始賦值依舊會(huì)被當(dāng)作正常的賦值操作執(zhí)行
*/
var value = 2;

console.log(value); // 2

變量聲明提升

var的變量聲明存在“提升”(hoisting)的特性。JavaScript在執(zhí)行函數(shù)代碼的前,首先會(huì)對(duì)函數(shù)內(nèi)當(dāng)前執(zhí)行上下文中的所有var的變量聲明進(jìn)行掃描確認(rèn),并將所有的聲明按順序提前到當(dāng)前執(zhí)行上下文的頂部,也就是函數(shù)內(nèi)的頂部。這種變量聲明提升的特性,讓我們?cè)谕蛔饔糜蛑械目梢灾苯釉L問和使用一個(gè)在后續(xù)的代碼才進(jìn)行了首次變量聲明的變量,即在變量聲明之前使用變量。

雖然,變量的聲明在執(zhí)行時(shí)被提升了,但是,變量聲明中的初始賦值操作卻并沒有被提升,從而形成一種聲明和初始賦值的執(zhí)行邏輯上的割裂。也就是說,即使我們可以直接訪問和使用在后續(xù)的代碼才被聲明和賦值的變量,然而,變量的值卻是undefined。代碼一直執(zhí)行到聲明和初始賦值語句后,變量的值才會(huì)被賦值為它的初始賦值。

/*
  由于變量聲明提升了,可以在變量聲明前訪問使用變量,
  但是,訪問到的變量值是undefined
*/
console.log(value); // undefined

/* 變量賦值并沒有被提升,只有執(zhí)行完這一句語句,變量才被賦值為1 */
var value = 1;

console.log(value); // 1

怪異危險(xiǎn)的var

var的這些的怪異的特性,雖然在程序上都屬于合法可運(yùn)行,但是,對(duì)于編寫程序的人來說,有著許多有違正常邏輯的地方,而且容易造成代碼調(diào)試的困難。例如:面對(duì)變量的聲明和管理,我們不得不以函數(shù)為基本的管理區(qū)進(jìn)行管理;對(duì)變量的重復(fù)聲明,讓我們對(duì)代碼從上而下的變量的安全訪問和變量值的確認(rèn)變得不容易控制;

雖然,為了防止這些危險(xiǎn)的出現(xiàn),我們可以通過編程習(xí)慣的方式來進(jìn)行約束。但是,這些終究只是軟約束,隨著JavaScript的不斷發(fā)展和使用來構(gòu)建復(fù)雜的應(yīng)用,這些約束就愈加捉襟見肘。

let和const的變量聲明

黑暗和混沌,終于迎來了曙光的到來。ECMAScript2015的到來,就像一束光輝照耀進(jìn)來,給JavaScript變量聲明這一塊帶來了翻天覆地的變化。不僅帶來了letconst這兩個(gè)新的變量聲明關(guān)鍵字,而且還直接一躍變成了最佳的變量聲明方式。

而這些驚天動(dòng)地變化的出現(xiàn)都是因?yàn)椋鄬?duì)于var,letconst帶來了如下新的共同特征:

塊級(jí)作用域

letconst關(guān)鍵字帶給我們的第一個(gè)讓人興奮的特性就是塊級(jí)作用域??嘤谥?code>var關(guān)鍵字的基于函數(shù)作用域的特性,我們不得在函數(shù)范圍內(nèi)小心的定義變量名稱和使用變量,特別是在條件判斷和循環(huán)邏輯中,甚至不得不在循環(huán)中使用上立即執(zhí)行函數(shù)來進(jìn)行變量值的讀取和保存。

function f() {
  /*
    由于var基于函數(shù)作用域,回調(diào)函數(shù)內(nèi)的i變量都為同一個(gè)副本,
    下面程序的輸出結(jié)果為5、5、5、5、5
  */
  for (var i = 0; i < 5; i++) {
    setTimeout(() => { console.log(i); });
  }
    
  /*
    通過立即執(zhí)行函數(shù)來構(gòu)建基本數(shù)據(jù)類型的值傳遞,從而創(chuàng)建新的變量副本,
    下面程序的輸出結(jié)果為0、1、2、3、4
  */
  for (var i = 0; i < 5; i++) {
    (function(i) {
      setTimeout(() => { console.log(i); }, 0);
    })(i);
  }
}

f();

現(xiàn)在,我們可以通過花括號(hào){}以及letconst關(guān)鍵詞聲明限制于塊級(jí)作用域中的變量了。if塊、while塊、function塊甚至單獨(dú)的{}都是良好的塊級(jí)作用域聲明區(qū)塊。還有一點(diǎn),相對(duì)于var,在全局作用域中使用letconst聲明的變量并不會(huì)添加到全局上下文對(duì)象global中。綜合看來,塊級(jí)作用域的特性使我們的變量的聲明和使用更加的簡(jiǎn)潔和安全。

/*
  由于使用的基于跨級(jí)作用域的let,每次循環(huán)體都會(huì)產(chǎn)生一個(gè)i變量的副本,
  下面程序的輸出結(jié)果為0、1、2、3、4
*/
for (let i = 0; i < 5; i++) {
  setTimeout(() => { console.log(i); }, 0);
}

if (true) {
  let a = 1;
}
/*
  拋出ReferenceError,
  因?yàn)橛胠et定義的a變量限制于塊級(jí)作用域,只在前面方括號(hào)中可以訪問
*/
console.log(a); // ReferenceError

/*
  let和const聲明在全局作用域中聲明的變量,不會(huì)被隱晦添加到global對(duì)象中,
  保證了全局環(huán)境的安全,
*/
let b = 1;
console.log(window.b); // undefined

不可重復(fù)聲明

相比于var的重復(fù)聲明會(huì)被忽略,letconst在同一作用域中對(duì)同一標(biāo)識(shí)符的變量不能重復(fù)聲明。這種變量聲明執(zhí)行上的約束,避免了同作用域下不同位置的變量聲明沖突,消除了許多不容易預(yù)見的運(yùn)行問題,讓JavaScript在構(gòu)建復(fù)雜應(yīng)用中的變量聲明過程變得更加的安全。

let a = 1;
{
  /* 不在同一塊級(jí)作用域內(nèi),而在子級(jí)塊級(jí)作用域內(nèi) */
  let a = 1;
}
/*
  和最上面的聲明在同一塊級(jí)作用域內(nèi),屬于重復(fù)聲明,
  這條語句執(zhí)行會(huì)拋出SyntaxError
*/
let a = 1; // SyntaxError

暫時(shí)性死區(qū)

相對(duì)于var的變量聲明提前導(dǎo)致的變量在函數(shù)作用域中可以“先使用后聲明”的現(xiàn)象。letconst關(guān)鍵字聲明的變量也具有執(zhí)行中變量聲明提前的特性,但是卻不能在變量完成聲明之前進(jìn)行訪問和修改,否則會(huì)拋出ReferenceError的錯(cuò)誤。

這種變量聲明所綁定的塊級(jí)作用域的頂部,一直到變量聲明語句執(zhí)行完成之前的,變量不能訪問和修改的區(qū)域,稱為這個(gè)變量的暫時(shí)性死區(qū)(TDZ,temporal dead zone)。暫時(shí)性死區(qū)的引入,在代碼執(zhí)行上約束了變量必須遵循“聲明后才能使用”的原則,使JavaScript的變量聲明和使用更加安全和具有更好的可讀性。

{
  /* === 變量a和b的TDZ start === */
  /*
    拋出ReferenceError,
    TDZ范圍內(nèi)不能讀取和修改變量b
  */
  console.log(b); // ReferenceError
  let a = 1;
  /* === 變量a的TDZ end === */
  let b = 2;
  /* === 變量b的TDZ end === */
  console.log(a); // 1
}

值得注意的是,暫時(shí)性死區(qū)是基于執(zhí)行順序(時(shí)間)上的,而不是編寫代碼順序(位置)上的。只要變量的訪問和修改的代碼的執(zhí)行,是在變量聲明之后,就是合法的。

{
  /* === 變量value的TDZ start ===*/
  function f() {
    console.log(value);
  }

  /* 在DZT內(nèi)訪問和修改變量value,會(huì)拋出錯(cuò)誤 */

  let value = 1;
  /* === 變量value的TDZ end ===*/
  /*
    函數(shù)f中對(duì)value的訪問發(fā)生在TDZ外面,
    所以訪問合法
  */
  f(); // 1
}

使用好let和const

letconst兩者的這些新的共同特性,讓它們直接變成了變量聲明的最佳方式和實(shí)踐。但是,兩者的除了上面說道的共同特性,還存在一個(gè)兩者的差異。

簡(jiǎn)單來說,letconst的區(qū)別在于,let用于聲明基于塊級(jí)作用域的變量,而const用于聲明基于塊級(jí)作用域的常量。

  • 使用let聲明的變量,在變量的整個(gè)生命周期中,能夠?qū)ψ兞侩S時(shí)進(jìn)行賦值修改。
  • 使用const聲明的變量為引用常量,必須在聲明的同時(shí)進(jìn)行初始賦值,在變量的整個(gè)生命周期中,無法再通過賦值的方式來修改變量值。

const的這個(gè)無法再賦值的特性,在不同的變量類型下會(huì)有不同的表現(xiàn):

  • 如果const聲明并且初始化賦值是基礎(chǔ)數(shù)據(jù)類型變量(String、Number、Boolean、Symbol),那么該變量之后就不能再進(jìn)行任何值的修改了。因?yàn)榛A(chǔ)數(shù)據(jù)類型變量的值必須通過變量賦值來進(jìn)行修改。
  • 如果const聲明并且初始化賦值是引用數(shù)據(jù)類型變量(ArrayObject、MapSet),那么我們則可以隨意對(duì)該變量的字段屬性進(jìn)行修改。因?yàn)橐脭?shù)據(jù)類型變量的內(nèi)容修改(新增、修改、刪除屬性)并不會(huì)出現(xiàn)所聲明變量的賦值操作.
/* let聲明的變量可以任意賦值和修改 */
let value1 = 1;
value1 = 2;

/*
  拋出SyntaxError,
  const聲明的變量必須賦值初始值
*/
const value2; // SyntaxError

/*
  拋出TypeError,
  const聲明的變量在整個(gè)變量生命周期內(nèi)不能再賦值
*/
const value3 = 3;
value3 = 4 // TypeError

/* const聲明的引用數(shù)據(jù)類型變量,依舊可以修改其字段值 */
const value4 = {
  key1: 1,
  key2: 2,
};
value4.key = 3;

在實(shí)踐中,我們應(yīng)該盡可能的多使用const進(jìn)行變量聲明,當(dāng)確定一個(gè)變量需要重新賦值的時(shí)候才將其改用let進(jìn)行聲明,這樣可以讓我們對(duì)程序執(zhí)行中的可能發(fā)生重新賦值的變量有一個(gè)清晰的了解,減少可能出現(xiàn)的bug。

總結(jié)

ECMAScript2015帶來了letconst這兩個(gè)新的變量聲明關(guān)鍵字,帶來了塊級(jí)作用域和暫時(shí)性死區(qū)等新特性,解決過去var的變量聲明中的一些怪異問題,在語法層面和運(yùn)行層面上,保證了變量聲明的“聲明后才可以使用”的安全特性,提高了JavaScript在編寫大型應(yīng)用時(shí)的變量聲明和使用安全。

當(dāng)前,letconst已經(jīng)是我們?nèi)粘i_發(fā)中的變量聲明的最佳時(shí)間方式。了解它們,才能用好它們。

參考資料

  • 《JavaScript高級(jí)程序設(shè)計(jì)》
  • 《你不知道的JavaScript》
  • MDN

到此這篇關(guān)于JavaScript變量聲明的var、let、const詳解的文章就介紹到這了,更多相關(guān)JS變量聲明var、let、const內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論