js典範面試題--變量提拔、實行環境、作用域鏈

js典範面試題–變量提拔、實行環境、作用域鏈

本日紀錄一個js的典範面試題,該編程題觸及到了js的變量提拔、實行環境、作用域鏈題目。

1、變量提拔
js沒有塊級作用域,運用var聲明的變量會自動添加到最接近的環境中。在函數內部,最接近的環境就是函數的部分環境。假如初始化變量時沒有運用var變量,該變量會自動被添加到全局環境。下面兩幅圖是等價的,結果都是控制台打印出1 2 3 4 5
《js典範面試題--變量提拔、實行環境、作用域鏈》

2、 實行環境
每一個函數都有本身的實行環境。當實行流進入一個函數時(即挪用該函數),函數的環境就會被推入一個環境棧中。而在函數實行以後,將其環境彈出棧,把控制權返回給之前的實行環境。全局實行環境是最外圍的一個實行環境。全局實行環境被認為是window對象,全局實行環境直到應用程序退出–比方封閉網頁或瀏覽器—時才會被燒毀。

function a(){
    //實行a功用代碼
  }
  a();       //函數a的環境被推入一個環境棧中。
  
  function b(){
    //實行b功用代碼
    var c=function(){
      //實行c功用代碼
      function d(){
        //實行d功用代碼
      }
      retrun d();
    }
    return c();
  }
  b();     //函數b、c、d順次被推入一個環境棧中,當挪用b()函數時,其順次被彈出

實在行的詳細流程如下圖所示:
《js典範面試題--變量提拔、實行環境、作用域鏈》

3、作用域鏈
當代碼在一個環境中實行時,會建立變量對象的一個作用域鏈。作用域鏈的用處,是保證對實行環境有權接見的一切變量和函數的有序接見。作用域鏈的前端,一直都是當前實行的代碼地點環境的變量對象。作用域鏈中的下一個變量對象來自包括(外部)環境,而再下一個變量對象則來自下一個包括環境。如許,一致延續到全局實行環境;全局實行環境的變量對象一直都是作用域鏈中的末了一個對象。

var a=1;
function b(){
    //實行b功用代碼
    var bVar=1;
    var c=function(){
      //實行c功用代碼
      var cVar=2;
      function d(){
        //實行d功用代碼
        var dVar=3;
        cVar=3;
      }
      retrun d();
    }
    return c();
 }
 b();  

《js典範面試題--變量提拔、實行環境、作用域鏈》

以上代碼共觸及4個實行環境:全局環境,b()的部分環境、c()的部分環境、d()的部分環境。全局環境有一個變量a和一個函數b()。b()的部分環境中有一個變量bVar和一個函數c…..順次。位於最裡邊的函數能夠接見外部環境的一切變量和函數,因為外部環境是它的父實行環境。總結:內部環境能夠經由過程作用域鏈接見一切的外部環境,但外部環境沒法接見到內部環境中的任何變量和函數。這些環境之間的聯絡是線性、有序次的。每一個環境都能夠向上搜刮作用域鏈,以查詢變量和函數名(恪守就近準繩);但任何環境都不能經由過程向下搜刮作用域而進入另一個實行環境。

經由過程上面引見實行環境與作用域的兩幅圖能夠看出,瀏覽器在實行js時,起首會將window對象(全局實行環境)壓入環境棧,每次實行一個函數時,被挪用的函數(根據挪用的先後順序)順次壓入環境棧中。而壓入棧中的環境類似於容器,往棧底方向的容器包括了上面的容器。容器中寄存的是本身的變量和函數以及上面的容器。我們能夠把容器的玻璃的材質設想為車窗戶(能夠从里邊看到表面,然則沒法從表面看到裡邊),當在某個環境(容器)中實行代碼塊時,就好比我們站在當前容器里,此時我們能夠看到外部容器(父級環境)的變量和函數,但卻看不到內部容器的任何東西,這就是作用域鏈。

下面進入正題,說下我對該面試題的明白

1    var foo = {n:1};
2    (function (foo) {
3        console.log(foo.n);
4        foo.n=3;
5        var foo = {n:2};
6        console.log(foo.n);
7    })(foo);
8    console.log(foo.n); 

上面的代碼實在能夠寫成如許:

1    var foo = {n:1};
2    (function (foo) {
3         var foo;
4        console.log(foo.n);
5        foo.n=3;
6        var foo = {n:2};
7        console.log(foo.n);
8    })(foo);
9    console.log(foo.n); 

1、聲明一個變量,為援用範例
2和8、聲明一個匿名函數,並馬上實行,通報的參數是第1行中的foo。將一個對象範例賦值給一個新的變量,因為對象是援用範例,實質上是指將對象的地點賦值給該變量(也就是說這兩個變量指向同一個地點空間),因而轉變新的變量中的屬性值或要領,對應的本來對象的值也會轉變。
3、原題中的第5行,因為存在變量提拔,因而會在函數最先就聲明,此時為undefined;但是因為一個變量的聲明優先級低於形參,所以這行沒有任何結果
4、打印形參的foo.n,打印1
5、轉變第1行變量foo的屬性n的值為3;
6、從新聲明並定義了一個變量,拓荒了新的內存空間,n為2
7、因為js中的代碼是自上而下實行,所以此時輸出2
9、上面的函數挪用完畢后,部分變量被燒毀,而之前的內存空間值已變成3,所以輸出3
所以終究的結果為:1 2 3

    原文作者:Alisa
    原文地址: https://segmentfault.com/a/1190000014437648
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞