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