前言:這是筆者學習之后自己的理解與整理。如果有錯誤或者疑問的地方,請大家指正,我會持續更新!
作用域、作用域鏈、執行環境、執行環境棧以及this的概念在javascript中非常重要,本人經常弄混淆,這里梳理一下;
作用域
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對象的所有屬性擁有全局作用域,在代碼任何地方都可以訪問;
函數內部聲明的變量就是局部變量,只能在函數體內使用,函數的參數雖然沒有使用var但仍然是局部變量。
<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>
作用域鏈
作用域鏈取決于函數被聲明時的位置,解析標識符的時候就先從當前作用域開始找,在當前作用域中無法找到時,引擎就會在外層嵌套的作用域中繼續查找,直到找到該變量,或抵達最外層的作用域(也就是全局作用域)為止;它的路線已經被定死了,和函數在哪里運行無關;
<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程序中的執行正是由這個機制控制著;
需要注意的是如果當前執行環境(存放當前作用域鏈里的數據和變量)找不到變量,那就是找不到了,不會往之前的那個執行環境查找,和作用域鏈是不一樣的;
代碼的執行順序也不全是一行一行的執行,而是和函數的調用順序有關:
<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>
以上代碼的執行順序:
代碼執行進入全局執行環境,并對全局執行環境中的代碼進入聲明提升;
執行第2行,賦值a=1; 然后第3行賦值b=2; 然后第4行賦值c=3; 然后第5行賦值d=4;
執行第20行,調用outer(999)函數,然后進入outer(999)函數執行環境,聲明提升,并將實參999傳給形參e;現在環境棧中有兩個執行環境,outer(999)是當前執行環境;
執行第16行,賦值a=5; 然后第17行賦值b=6;
執行第18行,調用inner(7)函數,然后進入inner(7)函數執行環境,聲明提升,并將實參7傳給形參d;
執行第7行,賦值c=8; 然后運算并輸出;
代碼優化
由于在作用域鏈上查找變量是需要消耗性能的,我們應該盡快的找到變量,所以在函數多層嵌套的時候,我們應盡可能的使用函數內部的局部變量;
我們在函數內部使用全局變量可以說是一種跨作用域操作,如果某個跨作用域的值在函數的內部被多次使用,那么我們就把它存儲到局部變量里,這樣可以提高性能。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答