JS閉包中的變量

1.變量的值

2017/09/28 補充
使用IIFE方式執行
for(var i = 0; i<3; i++){
(function (num){
console.log(num);
})(i)
}
不傳值的IIFE方式
for(var i = 0; i<3; i++){
(function (){
console.log(i);
})()
}

---------------------------------------------------------------------------------

先前在寫代碼的時候碰到一個問題,使用閉包來執行函數,會造成變量不能正常調用。

用一個例子來說明:

for(var i = 0; i<3; i++){
  function(){
    console.log(i);
  }
}
執行的結果並不是期望中的0,1,2;而是三個3。後來從我用了兩個月的時間才理解 let中找到了問題所在。這裏先放上地址:

https://zhuanlan.zhihu.com/p/28140450

根據文中所述,使用let創建變量,可以使得該變量在編譯階段生成多個隱藏的作用域中的變量,用來承載變量的臨時值。比如上面這個例子可以使用這樣的寫法:

for(let i = 0; i<3; i++){
  function(){
    console.log(i);
  }
}
僅僅改變一個變量創建的方式,就可以達到預期的效果。


這裏先不討論let具體的作用方式,而是探究爲何這個閉包不能起到預期的效果。

在JS中,一個對象有創建、初始化和賦值三個階段。所以,對於變量 而言,它在這段代碼中經歷瞭如下的過程:

1. 進入編譯環境中

2. 創建變量i

3. 首先將i初始化爲undefined

4. 執行代碼期間給i賦值

在這個過程中,可以發現在執行代碼之前要進行變量的創建和初始化,執行代碼後進行賦值。

而對於function,則是在執行之前就對聲明中的變量執行創建、初始化和賦值操作,內存中存儲的實際上是這個function執行過後才能得到的結果,供其他對象調用。自然,這裏的 i 也就在每一次調用的時候都變成了3,即執行過後的值。

而let能夠巧妙避開這裏面的弊端,得益於它每一次執行都使用一個隱藏的作用域中的變量來記錄變量的當前值,然後在真正執行的時候,依次指向這些隱藏的變量,而不是原本的那個變量,從而得到預期的結果。


2.跨作用域調用變量

這裏主要討論的是this指向的問題。在閉包中,跨作用域的變量是可以直接操作的,但是this比較特殊。

this的本質是一個指針,指向當前對象的作用域。然而在閉包中,this指向的並不是原本對象的作用域,而是閉包的作用域。也就是說,this是會動態變化的,在不同的作用域中有不同的指向,因而會造成跨作用域時失效。

一個簡單的解決方式是使用一個臨時變量,在對象作用域中(這個方法來自於《Learning TypeScript》一書)

let _this = this;
它解決了在閉包中找不到對象作用域的問題,以便閉包能對外部的變量也進行操作。


最後再摘錄一下《我用了兩個月的時間才理解 let》中對於變量提升的總結:

1. let 的「創建」過程被提升了,但是初始化沒有提升。
2. var 的「創建」和「初始化」都被提升了。
3. function 的「創建」「初始化」和「賦值」都被提升了。

4. const 和 let 只有一個區別,那就是 const 只有「創建」和「初始化」,沒有「賦值」過程。都被提升了。




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