JavaScript不一般的塊級作用域
不一般的塊級作用域
塊級作用域
1.什麼是作用域
由於程式碼執行會形成程式碼執行的空間,這個執行空間指的就是我們的作用域。 表示式,函式執行的環境就會產生作用域,也就是變數,函式能作用到的範圍,我在這個範圍內,起作用,他就是我的作用域。 通過之前的學習,瞭解過作用域,作用域鏈。 由於程式碼執行,在一“塊“內執行的程式碼,會產生作用域,也就是代表這段程式碼執行的空間在哪裡。 作用域存在的目的就是存放當前環境下,當前作用域下面的變數,函式等等。在ES6之前我們僅僅存在著全域性作用域,和函式作用域
全域性作用域:window環境下執行的程式碼。
var a = 10 function person() {} // window 作用域 中存在 a變數 以及 person這個函式 函式作用域: 由於函式執行,會產生作用域,這塊作用域記憶體放著當前函式內部執行的程式碼中的變數,函式。 function person() { var name = 'dxb' function getName() { console.log(name) } } person() //在這裡由於函式person執行,產生一塊作用域裡面包括name, getName 但是當person函式定義的時候就不會存裡面內容,儘管這個函式體是這樣的。
接下來看一個例子
function getScope() { var content = 'content' return function () { console.log(content) } } var content = getScope()()
//在這裡 getScope 執行會產生一個作用域,最後又返回了一個函式
//當被返回的函式被執行的時候訪問了一個叫 content 的變數。
// 但是被返回的函式在執行的時候並沒有 content , 咋辦?
// 它爸爸有啊,這裡也就是父級作用域的 content拿過來輸出一下。
// 又發現它父級作用域執行完了,並沒有釋放,對吧,這種現象也稱之為閉包。
所以作用域呈現鏈式,在函式執行的時候回反覆的建立作用域,但是每次建立的作用域都會至於作用域鏈的頂端,為了方便訪問當前作用域內的作用變臉以及函式,所以置於作用域鏈頂端。沒找到,會依次向下去找,知道作用域鏈訪問到頭,瀏覽器環境下的window還沒有找到想要的變數,那就報錯嘍, xxx is not defined
2.塊級作用域繫結
在過去 javascript的變數宣告機制大家都已經接觸過了,有什麼呢,變數宣告提升,函式宣告整體提升。而且有一些同學是從C語言學起的,對這樣宣告方式,用起來特別不舒服。ECMAScript6新的語法可以更好的控制作用域。塊級作用域的出現使得一系列的問題被解決。
回顧var宣告提升的機制,先看一段程式碼
if(false) { var name = 'wxb' } else { console.log("name 的值為" + name) } /* 結果是什麼呢? name 的值為 undefined 沒有報錯, 沒報錯的前提是什麼 a被定義 所以在這裡的實質就是 */ var name if(false) { var name = 'wxb' } else { console.log("name 的值為" + name) } // 在預編譯階段 變數進行的提升 // 接著在看一個例子 var name = ‘dxb’ // ......(此處省略10000行程式碼) 經過反覆的思考覺得姓王也不錯呢,而且將來可以發展為隔壁老王 var name = 'wxb' // 就把姓改了 姓王了 console.log(name) 結果 name = 'wxb' // 看到結果 鄧哥特別高興
從上面可以看出,通過var宣告變數有諸多不嚴謹的地方。 1、存在作用域問題。
2、允許重複定義,汙染同一作用域下的變數。 為了解決這個問題,引出塊級作用域,使得作用域的變數可以更好的把控
塊級作用域
特點: 塊級作用域宣告的變數只會在當前作用域及其以下的作用域可以訪問的到 存在於: {} 中,也被稱為塊中。
let 和 const 宣告 let 宣告變數的方式與 var 相同, 不同的是 通過 let 宣告的變數具有識別塊級作用域的能力, 同時不允許反覆宣告, 以及暫時性死區的特點 1. 具有識別塊級作用域的能力
eg : { let a = 10 var b = 10 } console.log(a, b) // a is not defined // 10
2. 不允許反覆宣告
eg: // 確實,在同一個世界下每個人都是不一樣的,而且目前沒有基因一模一樣的人 let p1 = '0x1234' // .... (此處省略10000行程式碼) 、、 let p1 = '0x1234' // 報錯 p1 has already been declared 3.暫存性死區 如果在當前作用域下,一定要先定義後使用 TDZ(temporal dead zone)
javascript搜尋引擎在掃描程式碼的時候發現變數宣告
1. 如果var 宣告的 放到當前作用域頂部,變數宣告提升
2. 如果是let、const宣告 會先放到TDZ中,當代碼執行的時候,遇到這個變數就報錯,如果遇到宣告就把他從TDZ中拿出來。 eg: typeof msg let msg // const 宣告變數 也具有 let 宣告變數的特點,被稱為 常量 // 接著在看下一個例子 // 鄧哥兒子為啥姓王啊,是有原因的 // 剛生下來確實姓鄧,但是鄧哥性格大家也都明白啊,穩久必浪,覺得現在政策寬鬆,隨便改性 var name = ‘dxb’ // ......(此處省略10000行程式碼) 經過反覆的思考覺得姓王也不錯呢,而且將來可以發展為隔壁老王 name = 'wxb' // 就把姓改了 姓王了 console.log(name) 結果 name = 'wxb' // 看到結果 鄧哥特別高興 可惜好景不長,國家下來政策,名字不能隨便亂改 // 聽說國家改政策了, 畢竟還是鄧哥自己的孩子(親不親生的就無從考證了,野史能寫 1PB = 1024TB = 1024 * 1024GB 量級的) const name = 'wxb' // 現在的名是 wxb 想改回鄧小寶 // .... (此處省略10000行程式碼) // 又託關係,又安排的,好不容易啊到了改名的地方 name = 'dxb' // 就想改個姓, 不行不讓改了 // 看似一個笑話,其實真實程度高達百分90%。 對於const 宣告的變數,我們成為常量 常量是不可以被改變的,但不是絕對的。 好比樓盤,同學們可能已經有房子的,或者正在找房子的。哈爾濱的房價20000。。。。 為什麼說是和樓盤去比。 當開發商批下來一塊地皮的時候, 只要技術夠,錢夠,你可以蓋很多層,但是,地皮位置不能變,不能說群力地段比哈西好,就換到群力去,不可以。 所以說她是可變的,維度可變,不可變,位置不變,其實對應的就是我們的物件 const obj = {} //這個時候地址批下來了,就在這 obj.name = 'wxb' // 我在這塊地皮內隨便搞 obj = {} // 覺得地段不行,想換。不好意思,不可以。
// Assignment to constant variable
經過上面的學習,介紹的塊級作用域,介紹了兩個生命變數的方式 let const,以及他們的特點,具有識別塊級作用域的能力,不可以在同一作用域內反覆宣告。 那麼接下來,咱們通過幾個小例子加深一下對於知識的理解與掌握
練習 eg: var a = {} console.log(window.a == a) //true let b = {} console.log(widow.b == b) // false console.log(window.b)
// undefined 說明 通過 var 宣告的變數會在 window 的物件上定義一個變數 a 通過 let const 宣告的變數不會在 window 的上定義物件
eg: for 迴圈中的 let 和 const 首先倆看一個簡單的例子 for(let i=0; i<10; i ++) { let i }
// 會報錯麼 let i for (let i=0; i< 10; i++) { }
// 會報錯麼 回想我們當初學閉包時的例子
var arr = [] for(var i=0; i<10; i++) { arr[i] = function () { console.log(i) } } arr[6]() // 10 console.log(i)
// 10 通過立即執行函式來解決。
我們之前在 for 迴圈使用 let 發現 每次迴圈中的通過let宣告的變數都可以執行不報錯 所以我們說每次迴圈的塊級作用域是不一樣的 之前通過閉包來解決的問題,通過let也可以解決 var arr = [] for(let i=0; i< 100; i++) { arr[i] = function () { console.log(i) } } arr[6] = 6
可能有同學不明白,怎麼就可以了呢。 每個let 定義的變數都不一樣,都在自己所在的塊級作用域內, 當函式執行的時候還要去拿i 所以每個作用域內的i不能被釋放,當函式執行的時候也就還能訪問。 那對於for in迴圈呢 同學們可以自己想一想。
最後給大家總結一下,塊級作用域的最佳實踐。隨著 let 宣告變數的方式逐漸被支援。這個let 更是大家想要的。對於需要被保護的變數通過 const 進行宣告。宣告的變數不會提升,僅僅在宣告的程式碼塊中使用,如此一來,javascript的宣告變數的方式和其他語言更相似了,同時也大幅降低產生錯誤的機率,雖說效能沒有之前好累,給我帶來了巨大的便利性。