深入理解JavaScript的函式作用域
什麼是作用域 ?
作用域:一個變數可以生效的範圍。 變數不是在所有地方都可以使用的,而這個變數的使用範圍就是我們要說的作用域。
注意:在JavaScript中,劃分作用域也是用大括號劃分的, 但是在 JS 之中能夠有效限定作用域的大括號只有函式大括號!
有哪些作用域 ?
- 全域性作用域(不再任何函式內)
學習使用作用域,以下內容是所需要了解的:
- 全域性作用域是最大的作用域
- 在全域性作用域中定義的變數可以在任何地方使用
- 頁面開啟的時候,瀏覽器會自動給我們生成一個全域性作用域 window
- 這個全域性作用域會一直存在,直到頁面關閉才會銷燬
請看下面示例程式碼:
1 var a = 10; 2 console.log(a); // 輸出結果: 10 3 function foo(){ 4 console.log(a); // 輸出結果: 10 5 } 6 foo()
當變數 a 宣告時,沒有被函式大括號包裹, 那麼這個變數我們稱之為 全域性變數
這個全域性變數在任何地方都可以訪問。
- 區域性作用域(在函式內部)
同樣的,以下內容也是所需要了解的:
- 區域性作用域就是在全域性作用域下面開闢出來的一個相對小一些的作用域
- 在區域性作用域中定義的變數只能在這個區域性作用域內部使用
- 在 JS 中只有函式能生成一個區域性作用域,別的都不行
- 每一個函式,都是一個區域性作用域
請看下面示例程式碼:
1 function foo(){ 2 var a = 10; //在大括號之中宣告的變數只能在這個大括號之中使用; 3 console.log(a); // 輸出結果: 10 4 } 5 foo(); 6 console.log(a); // 報錯 => ReferenceError: a is not defined
通過上面示例程式碼可以看出:
在函式大括號之中宣告的變數,這種變數我們稱之為 區域性變數 !
區域性變數只能在宣告它的作用域之中使用;
我們見慣了常規情況,現在看一個特殊情況:
宣告變數時不使用 var 關鍵字宣告:
注意!這是一個不規範的宣告方式!不要使用!不要使用!不用使用!
使用之後導致的結果就是:一個區域性變數的宣告,在全域性中也可以被訪問了!( 這樣的宣告叫做 偽全域性變數 )
請看下面示例程式碼:
1 function foo(){ 2 a = 10; // 此時宣告變數 a 沒有使用var關鍵字宣告 3 console.log(a); // 10 4 } 5 foo(); 6 console.log(a); // 輸出 10,不報錯了;
看完上面的程式碼示例,你是不是覺得這不挺好的嘛,變數 a 可以隨便使用,都不用報錯了,為什麼不讓使用??
呵呵,別高興太早,繼續往下看,你的這個想法很危險的!
當把區域性變數變成偽全域性變數:
你會發現:
- 生命週期變長,造成一定的負面影響;
- 佔據了全域性名稱空間,造成不可預知的錯誤
是不是腦子裡突然蹦出了好多問題:生命週期是什麼鬼?名稱空間又是什麼鬼??
好吧,上面這個內容我說早了,那麼接著往下看:
全域性變數和區域性變數的區別:
在這裡就來聊一下上面的生命週期和名稱空間吧!
生命週期(這個變數在記憶體之中存活的時間)
就是你啥時候可以訪問!
- 全域性變數:生命週期是和程式同步的, 程式不關閉,變數就一直存在;
導致的結果就是會讓程式變得更重! 如果可能,還是少設計一點全域性變數吧!對大家都好
- 區域性變數:生命週期是和函式執行同步的,函式執行結束變數就被刪除了;
現在知道啥是生命週期了吧,全域性變數多了,可能真的會影響到我們程式的執行效率,上面說的偽全域性變數就是這個道理。
我們看完了生命週期,再來一起看一下名稱空間吧!
名稱空間(變數名命名的唯一性)
- 全域性變數:名稱空間是唯一的,一個頁面只有一個
我們先來舉一個小例子,話不多說上程式碼:
1 var count = 10; 2 function foo(){ 3 // 我的程式碼 : 我的私人領域; 4 // 程式的懶惰原則:函式的大括號之中如果已經有了查詢結果,那麼就不會繼續再查找了; 5 // 就近原則; 6 var count = 0; 7 console.log(count); // 執行結果:0 8 } 9 foo(); 10 console.log(count); // 執行結果:10
為了解決全域性之中的名稱空間是唯一的這個問題,我們可以把這個變數放在區域性,那麼就不會佔用全域性的名稱空間了。
- 區域性變數:名稱空間一個作用域一個
可以用匿名函式來解決名稱空間的問題
最後一個問題:我現在要寫一大坨程式碼,和別人的程式碼要配合 。 這個時候咋寫呢???
我可以把程式碼寫在一個沒有名字的函式之中! 什麼?這樣寫直接報錯 ?
JS為啥阻止他 ? 如果我立即呼叫呢!來一個瞞天過海如何?
讓匿名函式經歷一次運算: 如果函式發生了特定的運算, 那麼這個函式就會被當做一個地址;
1 // 讓匿名函式經歷一次運算: 如果函式發生了特定的運算,那麼這個函式就會被當做一個地址; 2 // var res = 1 + function(){} 3 // console.log(res); //執行結果:1function(){} 4 5 // 利用這個機制使用匿名函式立即呼叫 : 6 // +function(){ 7 // console.log("hello world") 8 // }(); 9 10 // 函式經過運算變成了地址,被後面的呼叫運算子呼叫了; 11 // !function(){ 12 // console.log("hello world") 13 // }(); 14 15 // 一種普遍的寫法 : 16 17 // (function(){ 18 // console.log("hello world1"); 19 // })() 20 // 可能存在的bug; 21 // (function(){ 22 // console.log("hello world1"); 23 // })(); 24 // //兩個匿名函式同時是使用一定要加上分號 25 // (function(){ 26 // console.log("hello world2"); 27 // })(); 28 // 建議寫法; 29 ;(function(){ 30 console.log("hello world1"); 31 })();
最後來個總結吧!
1、全域性不能訪問區域性
2、區域性可以拿到全域性
喜歡的朋友可以點點關注,點點贊,歡迎評論區留言互