1.塊級作用域 什麼是: 在一個代碼塊(括在一對花括弧中的一組語句)中定義的所需變數(與let配合使用)併在代碼塊的外部是不可見的。 為什麼: 在ES6之前,函數只能在全局作用域和函數作用域中聲明,不能在塊級作用域中聲明,造成諸多問題: 1.內層變數會覆蓋外層變數 2.用來計數的迴圈變數泄漏變成全局 ...
1.塊級作用域
什麼是:
在一個代碼塊(括在一對花括弧中的一組語句)中定義的所需變數(與let配合使用)併在代碼塊的外部是不可見的。
為什麼:
在ES6之前,函數只能在全局作用域和函數作用域中聲明,不能在塊級作用域中聲明,造成諸多問題:
1.內層變數會覆蓋外層變數
2.用來計數的迴圈變數泄漏變成全局變數
何時:
需要強化對變數生命周期的控制,避免全局污染出現錯誤
優點:
1.阻止變數泄漏變為全局變數,造成全局污染
2.阻止變數提升覆蓋
3.內層作用域可以定義與外層作用域相同的變數名,但變數是獨立的。
4.允許任意嵌套,外層作用域無法讀取內層作用域的變數
塊級作用域:
{},if{},else{},while{},for{},swicth{}…
//塊級作用域{},與let配合使用 { //大括弧之間就是塊級作用域 var num=1;//還是全局變數,可以被外部訪問 let num=1;//局部變數,不可以被外部訪問,塊級作用域必須用let聲明 } console.log(num);//此時num已經不可以被訪問,說明塊級作用域生效 //內層作用域可以定義與外層作用域相同的變數名,允許任意嵌套 //但變數與變數之間是沒有關係的,都是獨立的個體。 { let aa=1; console.log(aa);//1 { let aa=11; console.log(aa);//11 { let aa=111; console.log(aa);//111 } } } console.log(aa);//此時是不能被訪問的,防止aa泄露為全局變數 //防止變數提升造成覆蓋 //雖然都是f,但是配合了let使用,{}內部變成了一個小個體,不會影響其他的f var f=17; { let f=28; console.log(f); } console.log(f); //for塊級作用域 //計算1-100之間所有的整數的和,使用var聲明 for (var i=1,sum=0;i<=100;i++){ sum+=i; } console.log(sum);//5050,可以訪問 console.log(i);//101,可以訪問 //使用let聲明變數 for (let i=1,sum=0;i<=100;i++){ sum+=i; } console.log(i);//此時i是不能被訪問的。 console.log(sum);//此時sum是不能被訪問的。 //根據需求得知,最後需要訪問的是sum,i需要釋放 //所以把sum單獨放在外面,sum就可以被訪問,而i會被釋放 var sum=0; for (let i=1;i<=100;i++){ sum+=i; } console.log(sum);
2.let聲明
什麼是:
專門代替var來聲明變數用的
為什麼:
var的問題:
1.聲明提前
2.沒有塊級作用域
3.造成全局污染
何時:
只要聲明變數都用let
優點:
1.阻止了聲明提前
2.添加了塊級作用域
3.成為局部變數,不會造成全局污染
原理:
let其實就是一個匿名函數自調用!
且let為了雙保險,其實在底層悄悄給變數改了名字,在變數前增加了_
let的小脾氣:
1.在相同作用域/塊內:禁止同時let兩個同名的變數
2.在let 變數 之前到當前作用域的頂部之間,不允許提前使用變數
3.不可以在函數內部重覆聲明參數
//1.簡單瞭解let聲明的變數 let a=2; console.log(a); let a=3; console.log(a);//報錯,a不能重覆聲明並賦值 //var聲明的變數可以重覆聲明並重新賦值 var b=2; console.log(b);//2 var b=3; console.log(b);//3 //在塊級作用域內{} { var c=2; let c=4;//在同一塊級作用域內不允許重覆聲明變數 let d=3; console.log(d);//只能在當前{}塊級作用域內訪問 } console.log(c);//可以被外部訪問的是var聲明的c console.log(d);//不可以被外部訪問 //let不允許先調用,後聲明 { console.log(aaa); let aaa=5;//報錯Cannot access 'aaa' before initialization }
把let放入實例中理解
//let應用 //累加每個任務函數的時間 var t=0; function task1(){ console.log(`任務1耗時3s`); t+=0.3; } function task2(){ console.log(`任務二耗時8s`); t+=0.8; } task1(); task2(); console.log(`共耗時${t}s`)以上是一個可以正常執行的代碼,並且是正確的程式 如果在task2內添加其他的功能,例如捕捉錯誤
//累加每個任務函數的時間 var t=0;//聲明變數t準備累加每個任務函數的時間 function task1(){ console.log(`任務1耗時3s`); t+=3; } function task2(){ console.log(`任務二耗時8s`); t+=8; //模擬出錯的變數,此段代碼是不執行的 var err=false; //如果出錯 if(err==true){ //就獲得出錯時間 var t=new Date(); //並輸出出錯提示日誌 console.log(`出錯啦,at:${t.toLocaleDateString()}`); }; } task1(); task2(); console.log(`共耗時${t}s`)代碼正常執行,if內代碼是不執行的,但是結果卻是錯誤的,少了8s,那麼為什麼少了8s呢? 原來是因為var聲明的變數是全局變數,而if{}不是作用域,沒有實體牆,攔不住var, 所以var聲明的變數會提升到當前作用域task2的最前面。
//累加每個任務函數的時間 var t=0;//全局t function task1(){ console.log(`任務1耗時3s`); t+=3; } function task2(){ //var t;//undefined,var聲明的t提前到該作用域最前面,並且沒有賦值 //如果task2()中已經有了局部變數t,就不會用全局的t了,只有在局部沒有的t的時候才會調用全局的t console.log(`任務二耗時8s`); //這個8s沒有加到全局t,而是加在局部t上,當函數調用後,局部的t就被釋放了 t+=8;//task2中的局部變數t,加在這裡 var err=false;//模擬出錯的變數 //如果出錯 if(err==true){//if else for while do whlie 等程式結構的{}不是作用域,不是實體牆,攔不住var //就獲得出錯時間 var t=new Date(); //並輸出出錯提示日誌 console.log(`出錯啦,at:${t.toLocaleDateString()}`); }; } task1(); task2(); console.log(`共耗時${t}s`)//此處輸出的是全局的t,所以沒有那8s,那麼鑒於這種明明沒有執行的代碼,卻破壞了原本正確的代碼, 這就是沒有塊級作用域帶來的危害,此時就需要使用let聲明變數了, 因為let會阻止聲明提前,並會添加塊級作用域
//累加每個任務函數的時間 var t=0;//全局t function task1(){ console.log(`任務1耗時3s`); t+=3; } function task2(){ console.log(`任務二耗時8s`); t+=8;//這個t還是會加到全局的t上,沒有被影響 //模擬出錯的變數 //var err=false;//代碼不執行 var err=true;//代碼執行 //如果出錯 if(err==true){//let將if{}也變成了一級作用域,這個作用域是有實體牆的,是可以攔住let聲明的變數的 //就獲得出錯時間 //let阻止了局部的t被聲明提前 let t=new Date();//此時的t是在這個if塊級作用域的函數內,不會存在提升 //並輸出出錯提示日誌 console.log(`出錯啦,at:${t.toLocaleDateString()}`);//所以此時輸出的t也是if內的t }; } task1(); task2(); console.log(`共耗時${t}s`)//列印全局tlet的原理,添加匿名函數自調用,並改變名字
//累加每個任務函數的時間 var t=0;//全局t function task1(){ console.log(`任務1耗時3s`); t+=3; } function task2(){ console.log(`任務二耗時8s`); t+=8;//這個t還是會加到全局的t上,沒有被影響 //var err=false; var err=true;//模擬出錯的變數 //如果出錯 if(err==true){//let將if{}也變成了一級作用域,這個作用域是有實體牆的,是可以攔住let聲明的變數的 //(function(){//let自動添加的 //就獲得出錯時間 //let阻止了局部的t被聲明提前 let t=new Date();//let悄悄改變了名字,變成了_t,此時的t是在這個if塊級作用域的函數內,不會存在提升到 //並輸出出錯提示日誌 console.log(`出錯啦,at:${t.toLocaleDateString()}`);//_t,所以此時輸出的t也是if內的t //})();//let自動加的 }; } task1(); task2(); console.log(`共耗時${t}s`)//列印全局t
單詞:
declare——聲明
access——訪問
initialization——初始化——>第一次聲明+賦值=初始化