前言:這是筆者學習以後本身的理解與整理。若是有錯誤或者疑問的地方,請你們指正,我會持續更新!javascript
做用域、做用域鏈、執行環境、執行環境棧以及 this 的概念在 javascript 中很是重要,本人常常弄混淆,這裏梳理一下:java
JavaScript 沒有塊級做用域的概念,只有函數級做用域:變量在聲明它們的函數體及其子函數內是可見的。函數
做用域就是變量和函數的可訪問範圍,控制着變量和函數的可見性與生命週期,在 JavaScript 中變量的做用域有全局做用域和局部做用域。性能
變量沒有在函數內聲明或者聲明的時候沒有帶 var 就是全局變量,擁有全局做用域。學習
<script type="text/javascript"> function test1(){ a = 1;//全局變量,只有在當前函數運行時,纔有效 } test1(); console.log(a);//1 注意test1函數必須運行,否則找不到a </script>
全局變量能夠當作 window 對象的屬性用,他們是同樣的。優化
<script type="text/javascript"> var b = 1;//全局變量 console.log(b === window.b);//true 全局變量能夠當作window對象的屬性用,他們是同樣的; </script>
window 對象的全部屬性擁有全局做用域,在代碼任何地方均可以訪問。this
函數內部聲明的變量就是局部變量,只能在函數體內使用,函數的參數雖然沒有使用 var 但仍然是局部變量。spa
<script type="text/javascript"> var c = 1;//全局變量 // console.log(d);//ReferenceError: d is not defined 引用錯誤,當前做用域就是最外層做用域,依然找不到d function test2(d){ console.log(c);//1 全局變量,哪均可以訪問;(先找當前做用域,找不到,就向外層做用域找,直到window最外層,找到了) console.log(d);//3 形參是局部變量,只有當前做用域下能夠訪問 } test2(3); </script>
做用域鏈取決於函數被聲明時的位置,解析標識符的時候就先從當前做用域開始找,在當前做用域中沒法找到時,引擎就會在外層嵌套的做用域中繼續查找,直到找到該變量,或抵達最外層的做用域(也就是全局做用域)爲止;它的路線已經被定死了,和函數在哪裏運行無關;code
<script type="text/javascript"> var a = 1; var b = 2; var c = 3; var d = 4; function inner(d) {//它的做用域鏈是inner---全局 var c = 8; console.log(a);//1 當前做用域找不到a,去全局做用域找到了a=1 console.log(b);//2 當前做用域找不到b,去全局做用域找到了b=2 console.log(c);//8 當前做用域找到了c=8 console.log(d);//7 當前做用域找到了d=7,形參也是局部做用域 // console.log(e);//ReferenceError: e is not defined 引用錯誤,找不到e, 它的做用域鏈是inner---全局 console.log(a+b+c+d);//18 } function outter(e) { var a = 5;//inner()的做用域鏈是inner---全局,因此這個a至關於無效 var b = 6;//inner()的做用域鏈是inner---全局,因此這個a至關於無效 inner(7); } outter(999);//這個999無效,裏面的e根本找不到 </script>
在多層的嵌套做用域中能夠定義同名的標識符,這叫做「遮蔽效應」,內部的標識符「遮蔽」了外部的標識符對象
經過 window.a 這種技術能夠訪問那些被同名變量所遮蔽的全局變量。但非全局的變量若是被遮蔽了,不管如何都沒法被訪問到
<script type="text/javascript"> var a = 'Lily'; var b = 'Lucy'; function outer() { var b = 'Jesica'; var c = 'Susan'; function inner(c) { console.log(a);//Lily console.log(window.b);//Lucy console.log(b);//Jesica console.log(c);//Jenifer } inner('Jenifer'); } outer(); </script>
執行環境(execution context),也叫執行上下文。每一個執行環境都有一個變量對象(variable object),保存函數可訪問的全部變量和數據(也就是函數的做用域鏈上的全部數據和變量)。咱們的代碼訪問不到它,它是給引擎使用的。
執行環境棧,當執行進入一個函數時,函數的執行環境就會被推入一個棧中。而在函數執行完以後,棧將其執行環境移除,它裏面的變量和數據會被標記清除,等待垃圾回收,再把控制權返回給以前的執行環境。javascript 程序中的執行正是由這個機制控制着。
須要注意的是若是當前執行環境(存放當前做用域鏈裏的數據和變量)找不到變量,那就是找不到了,不會往以前的那個執行環境查找,和做用域鏈是不同的;
代碼的執行順序也不全是一行一行的執行,而是和函數的調用順序有關:
1 <script type="text/javascript"> 2 var a = 1; 3 var b = 2; 4 var c = 3; 5 var d = 4; 6 function inner(d) {//它的做用域鏈是inner---全局 7 var c = 8; 8 console.log(a);//1 當前做用域找不到a,去全局做用域找到了a=1 9 console.log(b);//2 當前做用域找不到b,去全局做用域找到了b=2 10 console.log(c);//8 當前做用域找到了c=8 11 console.log(d);//7 當前做用域找到了d=7,形參也是局部做用域 12 // console.log(e);//ReferenceError: e is not defined 引用錯誤,找不到e, 它的做用域鏈是inner---全局 13 console.log(a+b+c+d);//18 14 } 15 function outter(e) { 16 var a = 5;//inner()的做用域鏈是inner---全局,因此這個a至關於無效 17 var b = 6;//inner()的做用域鏈是inner---全局,因此這個a至關於無效 18 inner(7); 19 } 20 outter(999);//這個999無效,裏面的e根本找不到 21 </script>
以上代碼的執行順序:
因爲在做用域鏈上查找變量是須要消耗性能的,咱們應該儘快的找到變量,因此在函數多層嵌套的時候,咱們應儘量的使用函數內部的局部變量;
咱們在函數內部使用全局變量能夠說是一種跨做用域操做,若是某個跨做用域的值在函數的內部被屢次使用,那麼咱們就把它存儲到局部變量裏,這樣能夠提升性能。