摘要:全局作用域在最頂層聲明的變量成為全局變量,全局變量擁有全局作用域,它們在程序的任何地方都是能夠被訪問到。作用域的主要作用是能夠控制變量是使用范圍。程序?qū)恢袛啵@個特性被稱為暫存死區(qū)。
1. 變量聲明、初始化
Javascript中使用一個變量之前需要先聲明,我們可以使用var、let、const來聲明一個變量。如果在給聲明的變量指定初始值,就是初始化。如:
var a = 1; let b =1; const c = "123"
初始化后,這個變量就在內(nèi)存中分配了空間,后面的程序就可以調(diào)用了。var、let、const都可以聲明變量,但是各自又不相同。在ES6中,const 定義的變量是必須要初始化賦值,而且以后不能變更, 是一個固定值。而像var,let是可以只聲明,但是不進(jìn)行初始化。
var a; let b; console.log(a) // => undefined console.log(b) // => undefined
而在我們熟知java代碼中,這樣只聲明,但是未賦值是會報錯的
int a; System.out.print(a); // The local variable a may not have been initialized
這是因?yàn)閖avascript的執(zhí)行引擎(例如V8)會在運(yùn)行這段代碼之前檢查會這段代碼, 也被稱為預(yù)編譯。發(fā)現(xiàn)有var和let聲明的變量,會給這個變量提供一個默認(rèn)的初始化值undefined。也正是因?yàn)橐獧z查代碼,所以像const所聲明的變量必須初始化、let,const所聲明的變量不能重復(fù)聲明。都是在檢查代碼時候,拋出錯誤。
const 必須初始化
console.log("hello world") const a // a 必須初始化,如果不初始化將會報 Missing initializer in const declaration
這段代碼在執(zhí)行的時候,沒有先打印"hello world", 而是直接報錯。我這里理解是代碼檢查的時候,就已經(jīng)把錯誤拋出來,程序沒有運(yùn)行.
let聲明后的變量不能重復(fù)聲明
let b = 1; console.log("hello world"); let b =2;
這段代碼在執(zhí)行的時候,沒有先打印"hello world", 而是直接報錯。我這里理解是代碼檢查的時候,就已經(jīng)把錯誤拋出來,程序沒有運(yùn)行.
一個變量的作用域是源程序代碼中定義這個變量的區(qū)域。
全局作用域:在最頂層聲明的變量成為全局變量,全局變量擁有全局作用域,它們在程序的任何地方都是能夠被訪問到。
函數(shù)作用域:在函數(shù)中聲明的變量只能在函數(shù)中被訪問到,函數(shù)外面是訪問不到的。
塊級作用域:任何一對花括號({和})中的語句集都屬于一個塊,在這之中定義的所有變量在代碼塊外都是不可見的,我們稱之為塊級作用域。
作用域的主要作用是能夠控制變量是使用范圍。
在Javascript中,var聲明的變量使用的是函數(shù)作用域,而let聲明的變量使用的塊級作用域。ES5中已經(jīng)存在var來聲明變量,那為啥到ES6還要使用let來聲明變量?我個人覺得是能夠更好的規(guī)范我們程序代碼,避免出現(xiàn)出現(xiàn)一些我們所無法預(yù)料的錯誤。
經(jīng)典例子如下:
var arr = []; for(var i = 0; i < 10; i++) { var j = i; arr[i] = function() { console.log(j) } } arr[0](); // =>9 而我們預(yù)想的是0
而使用let就能避免出現(xiàn)這樣的問題
var arr = []; for(var i = 0; i < 10; i++) { let j = i; arr[i] = function() { console.log(j) } } arr[0]() // =>0
這是因?yàn)閘et聲明的變量屬于塊級作用域,上面let所聲明的變量j是只能在for循環(huán)每一個{}中能被訪問。大致流程如下
當(dāng) i = 0,大致生成這樣一個塊
{ let j = 0; arr[i] = function() { console.log(j) } }
當(dāng)i = 1
{ let j = 1; arr[i] = function() { console.log(j) } }
. . . . . .
當(dāng)i = 9
{ let j = 10; arr[i] = function() { console.log(j) } }
當(dāng)后面去調(diào)用arr[0]()這個方法, 這是第一個塊定義的方法, 因?yàn)閘et聲明的變量j是塊{}作用域, 這個方法就只能訪問第一個塊{}中j的值,也就是0;
而當(dāng)我們使用var的時候
當(dāng) i = 0
{ var j = 0; arr[i] = function() { console.log(j) } }
當(dāng) i = 1
{ var j = 1; arr[i] = function() { console.log(j) } }
當(dāng)我們調(diào)用arr[0]()這個方法, 是第一個塊定義的方法,因?yàn)関ar聲明的變量是函數(shù)作用域,
所以上面的程序,可以理解為
var j; // var 聲明的變量具有聲明提前的作用 var arr = []; { j = 0; arr[i] = function() { console.log(j) } } { j = 1; arr[i] = function() { console.log(j) } } **. . . . . .** { j = 9; arr[i] = function() { console.log(j) } } arr[0](); // =>9
arr[0]()運(yùn)行的時候,由于沒有作用域的限制,j已經(jīng)被賦值為9。所以輸出結(jié)果就是9
有人會疑問,為啥不能直接使用i,而是使用中間變量j去替換?
這里是為了讓大家更好的理解,下面直接使用i
當(dāng)使用var聲明變量var arr = []; for(var i = 0 ; i < 10; i++) { arr[i] = function() { console.log(i); } } arr[0](); // => 10換成let聲明變量
var arr = []; for(let i = 0 ; i < 10; i++) { arr[i] = function() { console.log(i); } } arr[0](); // => 0 這是正常的
這樣也是正常的符合預(yù)期,但是當(dāng)我把代碼做了修改后
var arr = []; let i; for(i = 0 ; i < 10; i++) { arr[i] = function() { console.log(i); } } arr[0](); // => 10
這里也是let聲明的變量i,但是輸出的結(jié)果是10,這里我看完后很不理解。于是去閱讀了ES的規(guī)范 http://www.ecma-international...
這里是被認(rèn)為第一種情況。let聲明的i和var聲明i在上面的代碼執(zhí)行的邏輯是一樣的,就導(dǎo)致上面的情形。這里小伙伴們需要特別注意。
3. let暫存死區(qū),var聲明提前先從一個簡單的示例開始吧
執(zhí)行代碼:
console.log(a);
瀏覽器運(yùn)行結(jié)果如下:
執(zhí)行代碼:
if (false) {var b = 1}; console.log(b);
瀏覽器運(yùn)行結(jié)果如下:
執(zhí)行代碼:
if (false) {let c = 1}; console.log(c);
瀏覽器運(yùn)行結(jié)果如下:
第一種情形下: 當(dāng)我們直接在瀏覽器輸入未聲明的變量a, 有報 a is not defined的錯誤。
第二種情形下: b => undefined
第三種情形下: c is not defined
第二種情形下,變量在聲明之前就已經(jīng)能夠被使用,這種特性被稱非官方的稱為聲明提前。變量能夠提前到哪里呢?所有使用var聲明的變量被提前至函數(shù)頂部。
于是面試中常用這樣的問題出現(xiàn)
var global = "global" function test () { console.log(global); var global = "local"; console.log(global); } test() // undefined, local
這是因?yàn)樵趖est函數(shù)中,var聲明的global被提前到test函數(shù)頂部,注意聲明提前是在預(yù)編譯時候執(zhí)行的,并且給global初始化賦值undeifined。所以實(shí)際執(zhí)行的邏輯,應(yīng)該是這樣
var global = "global"; function test() { var global = undefined; console.log(global); global = "local"; console.log(global); } test(); //undefined, local
變量聲明提前好不好,很明顯,這樣很不好。一個變量還沒有聲明就能夠被使用,太奇怪了,也太危險了。尤其是那些習(xí)慣了java、c的人看起來,這就是一個異類的特性。
所以,在ES6中,就推薦使用了let來聲明變量。
作用域內(nèi) let 聲明的變量不能在聲明之前被使用,如果發(fā)現(xiàn)在let聲明的變量在聲明之前被使用,將會拋出 is not defined的錯誤。程序?qū)恢袛啵@個特性被稱為暫存死區(qū)。關(guān)于暫存死區(qū),我沒有看過V8的源碼,不過有個大膽的猜想
在 let 變量聲明的作用域內(nèi),javaScript引擎會把let聲明的變量名收集起來。當(dāng)程序開始執(zhí)行的時候,代碼一行行的往下執(zhí)行,引擎獲取變量之前,首先會判斷此變量的變量名是否是在let聲明的變量集合中。不存在,程序就繼續(xù)往下執(zhí)行。如果存在,就判斷變量名的上一個字符串是不是let。如果是let,就把此變量名從let聲明的集合中去掉,如果不是就拋出錯誤,程序被中斷。
(純屬于個人猜想,方便理解,大家不作為參考)
console.log("hello world"); c = 1; let c;
這里是先打印"hello world",然后再進(jìn)行報錯,說明是程序在執(zhí)行到 c = 1的時候報錯。
是運(yùn)行的錯誤。而大家可以對比前面的let聲明同一個變量多次,是程序還沒有執(zhí)行就報錯。所以這里是運(yùn)行中的錯誤??梢酝茢喑鲎兞繒捍嫠绤^(qū)是發(fā)生程序運(yùn)行中產(chǎn)生的,let聲明的變量不能在聲明之前使用,發(fā)現(xiàn)有聲明之前使用的,就會拋出錯誤,中斷程序。
另外一個程序
let x = x; // x is not defined x // x is not defined let x // Uncaught SyntaxError: Identifier "x" has already been declared
let x = x, 賦值從右往左執(zhí)行,所以先獲取變量 x, 于是x陷入暫存死區(qū)。拋出x is not defined的錯誤,程序中斷。后面再獲取x,還是拋出x is not defined,x還是陷入暫存死區(qū)。后面的let x,拋出Uncaught SyntaxError: Identifier "x" has already been declared 錯誤是因?yàn)闄z查這段代碼的時候,發(fā)現(xiàn)let x 已經(jīng)被聲明,于是拋出錯誤。不能被重復(fù)聲明了。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/93066.html
摘要:規(guī)范對其是這樣進(jìn)行的描述的。聲明定義了在正在運(yùn)行的執(zhí)行上下文作用域內(nèi)的變量環(huán)境中的變量。在執(zhí)行時,由帶有的定義的變量被賦其設(shè)定項(xiàng)的的值。由于變量已經(jīng)被聲明,是可訪問的,因此會打印出正確的結(jié)果。 你想在在變量聲明之前就使用變量?以后再也別這樣做了。 新的聲明方式(let,const)較之之前的聲明方式(var),還有一個區(qū)別,就是新的方式不允許在變量聲明之前就使用該變量,但是var是可以...
摘要:但對于引用類型的數(shù)據(jù)主要是對象和數(shù)組,變量指向的內(nèi)存地址,保存的只是一個引用地址指針,只能保證這個引用地址指針是固定的,至于它指向的堆內(nèi)存中的存儲的值是不是可變的,就完全不能控制了。 基礎(chǔ)概念 變量是存儲信息的容器,這里需要區(qū)分一下:變量不是指存儲的信息本身,而是指這個用于存儲信息的容器,可以把變量想象成一個個用來裝東西的紙箱子 變量需要聲明,并且建議在聲明的同時進(jìn)行初始化,如下所...
摘要:根據(jù)調(diào)查,自年一來,是最流行的編程語言。在一個函數(shù)體中聲明的變量和函數(shù),周圍的作用域內(nèi)無法訪問。也就是說被大括號包圍起來的區(qū)域聲明的變量外部將不可訪問。一個常見的誤解是使用聲明的變量,其值不可更改。 譯者按: 總結(jié)了大量JavaScript基本知識點(diǎn),很有用! 原文: The Definitive JavaScript Handbook for your next developer ...
var 用var申明一個變量: var a = 1; console.log(a) // 1 console.log(a) // undefined var a = 1; js的申明過程: var a; // undefined,只申明,不賦值。會有個默認(rèn)值undefined a = 1 // 1 例子: var a = 1; var a; console.log(a) // 1, 對重復(fù)定義且未...
摘要:語法和數(shù)據(jù)類型正文開始本章節(jié)復(fù)習(xí)的是中的基本語法,變量聲明,數(shù)據(jù)類型和字面量。聲明一個塊作用域的局部變量,可賦一個初始值。變量聲明有三種方式如,聲明局部變量和全局變量。 最近開始把精力放在重新復(fù)習(xí)JavaScript的基礎(chǔ)知識上面,不再太追求各種花枝招展的前端框架,框架再多,適合實(shí)際項(xiàng)目才是最重要。 上星期在掘金發(fā)布了幾篇文章,其中最大塊算是 【復(fù)習(xí)資料】ES6/ES7/ES8/ES...
閱讀 1532·2023-04-25 18:34
閱讀 3703·2021-11-19 09:40
閱讀 2888·2021-11-17 09:33
閱讀 3045·2021-11-12 10:36
閱讀 2937·2021-09-26 09:55
閱讀 2715·2021-08-05 10:03
閱讀 2580·2019-08-30 15:54
閱讀 2928·2019-08-30 15:54