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