JavaScript單線程,異步以及let const var的討論

在es6之前,我們定義變量都是用的var的方式,但是我確實花了不少的時間理解var的變量定義方式,var定義變量的作用域是整個函數範圍,而不是類似於c語言的在一個大括號內,嚴謹的說也就是沒有所謂的“塊級作用域”,這被視爲是JavaScript的一個缺點,如下代碼:

  for(var i=0;i<3;i++)
       {
       setTimeout(function(){
       console.log(i);
       },200)
       }//結果爲3,3,3


在瞭解到閉包,垃圾回收機制之前我對這個結果並不是很理解,但是現在回過頭來看就比較清晰了,當然,用到了setTimeout,就必須得先扯開一下聊聊JavaScript的語言特性,首先

它是一門單線程,異步的語言,所謂單線程,就是說JavaScript只能再一個時間內做一件事,舉個實際的例子來說,昨天早上我起牀叫我室友打LOL,他先起牀,後刷牙,耗時7分鐘,最後打開電腦等待遊戲登陸的3分鐘,總耗時10分鐘。這樣明顯會使得做事的效率明顯比較低,於是他今天早上學聰明瞭,起牀直接打開遊戲登陸,再去刷牙,只用了7分鐘,節約了3分鐘寶貴的遊戲時間,這也就是“異步操作”。那麼JavaScript到底是如何做到的呢,實際上,JavaScript,c語言(其他語言我也不曉得~~)的函數的編譯原理都是基於“堆棧”這種數據結構的,編譯器會把需要執行的函數放入stack中執行則入棧,return則出棧,所以對於某些函數,方法,JavaScript會讓它處於等待狀態,不會等到它出棧後再讓後續函數入棧。

其次:

在JavaScript中有垃圾回收機制,由於對地址的回收,類似於c語言的free函數,至少對於初學者,這是一個優點,但是便利的結果往往是更加糾結,關於這個有趣的機制MDN給出了詳細的解釋

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Memory_Management

由此可見,在我們給出的這個例子裏,i實際上是一個引用也就是指針,for循環先執行完畢後定時器函數執行,當定時器函數執行時,由於它的內部沒有定義i,所以他會沿着他的作用域鏈尋找i,而此時for循環已經被垃圾回收了,所以i最後的值爲for循環結束累加的值;

如何解決這個問題呢,此前我們會利用閉包的方法,將函數嵌套,讓i存在引用的對象,也就不會被垃圾回收了,代碼如下:

 

for(var i=0;i<3;i++){
      (function(){
           var fn1=(x)=>{
           console.log(x);
       }
       return fn1(i);
      })()
     }//0,1,2


var某些特性解釋讓代碼看起來如此複雜~~~;

那麼let將如何解決這個問題呢?

for(let i=0;i<3;i++){
        setTimeout(function(){
            console.log(i);
        })
       }//0,1,2


是不是感覺柳暗花明又一村的,和c語言寫循環是一樣的,明顯的感覺到JavaScript與其他語言相比其獨具風格的地方在變少;

綜上所述:

let,const的與var的區別在於,var的作用域在整個函數,而前者的作用域僅在父級的大括號;

當然還有其他的小區別:

1:var 會在定義的函數未執行時就被找到,如

     

  alert(i);//undefined
  var i=2;


 而換成let const就會報錯

2: const 定義的變量爲常量,不可以改變,會報錯,同樣用let重定義變量會拋出一個語法錯誤;

3:形如for (let x...)的循環在每次迭代時都爲x創建新的綁定。

/*也就是說循環創建的每一個i並不等效,i的值的改變並不是在單純的在原引用上改變;

參考資料

http://www.infoq.com/cn/articles/es6-in-depth-let-and-const/

https://blog.csdn.net/qq_22855325/article/details/72958345

 

---------------------文章轉載至:  用月光取暖----------------------------

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章