溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

javascript作用域鏈與執(zhí)行環(huán)境的示例分析

發(fā)布時(shí)間:2021-08-07 09:53:11 來源:億速云 閱讀:124 作者:小新 欄目:web開發(fā)

這篇文章主要介紹javascript作用域鏈與執(zhí)行環(huán)境的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

作用域、作用域鏈、執(zhí)行環(huán)境、執(zhí)行環(huán)境棧以及this的概念在javascript中非常重要,本人經(jīng)常弄混淆,這里梳理一下;

  • 局部作用域函數(shù)內(nèi)部的區(qū)域,全局作用域就是window;

  • 作用域鏈取決于函數(shù)被聲明時(shí)的位置,解析標(biāo)識符的時(shí)候就先找當(dāng)前作用域,再向外查找,直到全局,這樣一個(gè)順序;和函數(shù)在哪里調(diào)用無關(guān);

  • 執(zhí)行環(huán)境就是函數(shù)可訪問的數(shù)據(jù)和變量的集合,也就是函數(shù)的作用域鏈上的所有數(shù)據(jù)和變量;

  • 執(zhí)行環(huán)境棧就是根據(jù)代碼執(zhí)行順序,各執(zhí)行環(huán)境按照棧的形式逐層訪問,并且用完了退出來扔掉;如果當(dāng)前執(zhí)行環(huán)境(存放當(dāng)前作用域鏈里的數(shù)據(jù)和變量)找不到變量,那就是找不到了,不會往之前的那個(gè)執(zhí)行環(huán)境查找,它和作用域鏈?zhǔn)遣煌模?/p>

  • this是一個(gè)對象,它取決于是誰執(zhí)行的,誰執(zhí)行那就是誰;(this的概念還是不太清楚,這里寫的有點(diǎn)萬金油,過兩天再來修正) 

作用域

  JavaScript沒有塊級作用域的概念,只有函數(shù)級作用域:變量在聲明它們的函數(shù)體及其子函數(shù)內(nèi)是可見的。

  作用域就是變量和函數(shù)的可訪問范圍,控制著變量和函數(shù)的可見性與生命周期,在JavaScript中變量的作用域有全局作用域和局部作用域。

變量沒有在函數(shù)內(nèi)聲明或者聲明的時(shí)候沒有帶var就是全局變量,擁有全局作用域;

 <script type="text/javascript">
 function test1(){
  a = 1;//全局變量,只有在當(dāng)前函數(shù)運(yùn)行時(shí),才有效
  }
 test1();
 console.log(a);//1 注意test1函數(shù)必須運(yùn)行,不然找不到a
 </script>

全局變量可以當(dāng)做window對象的屬性用,他們是一樣的; 

<script type="text/javascript"> 
 var b = 1;//全局變量
 console.log(b === window.b);//true 全局變量可以當(dāng)做window對象的屬性用,他們是一樣的;
</script>

window對象的所有屬性擁有全局作用域,在代碼任何地方都可以訪問;

函數(shù)內(nèi)部聲明的變量就是局部變量,只能在函數(shù)體內(nèi)使用,函數(shù)的參數(shù)雖然沒有使用var但仍然是局部變量。

 <script type="text/javascript"> 
   var c = 1;//全局變量
//   console.log(d);//ReferenceError: d is not defined 引用錯(cuò)誤,當(dāng)前作用域就是最外層作用域,依然找不到d
   function test2(d){
    console.log(c);//1 全局變量,哪都可以訪問;(先找當(dāng)前作用域,找不到,就向外層作用域找,直到window最外層,找到了)
    console.log(d);//3 形參是局部變量,只有當(dāng)前作用域下可以訪問
   }
   test2(3);
  </script>

作用域鏈

  作用域鏈取決于函數(shù)被聲明時(shí)的位置,解析標(biāo)識符的時(shí)候就先從當(dāng)前作用域開始找,在當(dāng)前作用域中無法找到時(shí),引擎就會在外層嵌套的作用域中繼續(xù)查找,直到找到該變量,或抵達(dá)最外層的作用域(也就是全局作用域)為止;它的路線已經(jīng)被定死了,和函數(shù)在哪里運(yùn)行無關(guān);

<script type="text/javascript">
   var a = 1;
   var b = 2;
   var c = 3;
   var d = 4;
   function inner(d) {//它的作用域鏈?zhǔn)莍nner---全局
    var c = 8;
    console.log(a);//1 當(dāng)前作用域找不到a,去全局作用域找到了a=1
    console.log(b);//2 當(dāng)前作用域找不到b,去全局作用域找到了b=2
    console.log(c);//8 當(dāng)前作用域找到了c=8
    console.log(d);//7 當(dāng)前作用域找到了d=7,形參也是局部作用域
   // console.log(e);//ReferenceError: e is not defined 引用錯(cuò)誤,找不到e, 它的作用域鏈?zhǔn)莍nner---全局
    console.log(a+b+c+d);//18
   }
   function outter(e) {
    var a = 5;//inner()的作用域鏈?zhǔn)莍nner---全局,所以這個(gè)a相當(dāng)于無效
    var b = 6;//inner()的作用域鏈?zhǔn)莍nner---全局,所以這個(gè)a相當(dāng)于無效
    inner(7);
   }
   outter(999);//這個(gè)999無效,里面的e根本找不到
</script>

在多層的嵌套作用域中可以定義同名的標(biāo)識符,這叫作“遮蔽效應(yīng)”,內(nèi)部的標(biāo)識符“遮蔽”了外部的標(biāo)識符

通過window.a這種技術(shù)可以訪問那些被同名變量所遮蔽的全局變量。但非全局的變量如果被遮蔽了,無論如何都無法被訪問到;

<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>

執(zhí)行環(huán)境

  執(zhí)行環(huán)境(execution context),也叫執(zhí)行上下文。每個(gè)執(zhí)行環(huán)境都有一個(gè)變量對象(variable object),保存函數(shù)可訪問的所有變量和數(shù)據(jù)(也就是函數(shù)的作用域鏈上的所有數(shù)據(jù)和變量)。我們的代碼訪問不到它,它是給引擎使用的;

  執(zhí)行環(huán)境棧,當(dāng)執(zhí)行進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的執(zhí)行環(huán)境就會被推入一個(gè)棧中。而在函數(shù)執(zhí)行完之后,棧將其執(zhí)行環(huán)境移除,它里面的變量和數(shù)據(jù)會被標(biāo)記清除,等待垃圾回收,再把控制權(quán)返回給之前的執(zhí)行環(huán)境。javascript程序中的執(zhí)行正是由這個(gè)機(jī)制控制著;

  需要注意的是如果當(dāng)前執(zhí)行環(huán)境(存放當(dāng)前作用域鏈里的數(shù)據(jù)和變量)找不到變量,那就是找不到了,不會往之前的那個(gè)執(zhí)行環(huán)境查找,和作用域鏈?zhǔn)遣灰粯拥模?/strong>

  代碼的執(zhí)行順序也不全是一行一行的執(zhí)行,而是和函數(shù)的調(diào)用順序有關(guān):

  • 代碼進(jìn)入全局執(zhí)行環(huán)境,全局執(zhí)行環(huán)境放入環(huán)境棧;

  • 當(dāng)執(zhí)行到一個(gè)函數(shù)時(shí),就把這個(gè)函數(shù)的執(zhí)行環(huán)境推入到環(huán)境棧頂端,之前的執(zhí)行環(huán)境往后;

  • 全局執(zhí)行環(huán)境最先進(jìn)入,所以一直在底端;就和棧的概念差不多;

  • 函數(shù)執(zhí)行完之后,再把它的執(zhí)行環(huán)境從作用域鏈頂端移除,它保存的數(shù)據(jù)和函數(shù)都被標(biāo)記清除,等待垃圾回收;

  • 控制權(quán)交給之前的執(zhí)行環(huán)境,繼續(xù)往下執(zhí)行;

  • 當(dāng)頁面關(guān)閉時(shí),全局執(zhí)行環(huán)境才銷毀;

<script type="text/javascript">
 var a = 1;
 var b = 2;
 var c = 3;
 var d = 4;
 function inner(d) {//它的作用域鏈?zhǔn)莍nner---全局
  var c = 8;
  console.log(a);//1 當(dāng)前作用域找不到a,去全局作用域找到了a=1
  console.log(b);//2 當(dāng)前作用域找不到b,去全局作用域找到了b=2
  console.log(c);//8 當(dāng)前作用域找到了c=8
  console.log(d);//7 當(dāng)前作用域找到了d=7,形參也是局部作用域
 // console.log(e);//ReferenceError: e is not defined 引用錯(cuò)誤,找不到e, 它的作用域鏈?zhǔn)莍nner---全局
  console.log(a+b+c+d);//18
 }
 function outter(e) {
  var a = 5;//inner()的作用域鏈?zhǔn)莍nner---全局,所以這個(gè)a相當(dāng)于無效
  var b = 6;//inner()的作用域鏈?zhǔn)莍nner---全局,所以這個(gè)a相當(dāng)于無效
  inner(7);
 }
 outter(999);//這個(gè)999無效,里面的e根本找不到
</script>

以上代碼的執(zhí)行順序:

代碼執(zhí)行進(jìn)入全局執(zhí)行環(huán)境,并對全局執(zhí)行環(huán)境中的代碼進(jìn)入聲明提升;    
執(zhí)行第2行,賦值a=1; 然后第3行賦值b=2; 然后第4行賦值c=3; 然后第5行賦值d=4;
執(zhí)行第20行,調(diào)用outer(999)函數(shù),然后進(jìn)入outer(999)函數(shù)執(zhí)行環(huán)境,聲明提升,并將實(shí)參999傳給形參e;現(xiàn)在環(huán)境棧中有兩個(gè)執(zhí)行環(huán)境,outer(999)是當(dāng)前執(zhí)行環(huán)境;
執(zhí)行第16行,賦值a=5; 然后第17行賦值b=6;    
執(zhí)行第18行,調(diào)用inner(7)函數(shù),然后進(jìn)入inner(7)函數(shù)執(zhí)行環(huán)境,聲明提升,并將實(shí)參7傳給形參d;
執(zhí)行第7行,賦值c=8; 然后運(yùn)算并輸出; 

代碼優(yōu)化

  由于在作用域鏈上查找變量是需要消耗性能的,我們應(yīng)該盡快的找到變量,所以在函數(shù)多層嵌套的時(shí)候,我們應(yīng)盡可能的使用函數(shù)內(nèi)部的局部變量;

  我們在函數(shù)內(nèi)部使用全局變量可以說是一種跨作用域操作,如果某個(gè)跨作用域的值在函數(shù)的內(nèi)部被多次使用,那么我們就把它存儲到局部變量里,這樣可以提高性能。

以上是“javascript作用域鏈與執(zhí)行環(huán)境的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

js
AI