1. 程式人生 > >ES6—帶預設值的函式引數及其作用域

ES6—帶預設值的函式引數及其作用域

在學習ES6函式一章時,發現了一個有意思的現象,原文描述如下:

這段話主要state了3個事實:

①函式引數有預設值時,會在宣告初始化階段形成一個單獨的作用域

②這個作用域在初始化結束後消失

③沒預設值的情況下,沒有①②的現象發生。

這就很有意思了,我們一般說函式作用域,一般就是和全域性作用域、區域性作用域相愛相殺,這下來了個“宣告時作用域”,更熱鬧了,這3者之間到底什麼關係呢?

讓demo自己來說話吧。

例子1:

    var x = 10;
    function f( x,y=()=>{x=20;console.log(x);} ){//2
        console.log( x );//1
y();
var x = 30; console.log( x );//3 } f(); console.log( x );//4

大家覺得上述程式碼執行會輸出什麼呢?不賣關子了,直接看結果再來分析吧。

結果從上到下分別對應程式碼裡面的1、2、3、4輸出語句。在主句分析以前,我們先回顧一下 “函式內變數提前宣告” 的知識和上述的 “函式宣告作用域”的概念,基於這些理論,我們可以得出如下作用域模型:

在此基礎上,我們分析每個結果的由來。

//1: 函式體內聲明瞭與引數同名的變數x,此時根據“變數提前”原則,函式作用域內的x被提前到function頭部宣告,此時引數x也無預設值傳遞給它所以,函式內的x是undefined.

//2. 有預設值的函式作用域,在宣告是,分別開闢了自己獨有的存放x,y的作用域,這裡的x與函式體內的x沒任何關係,有值的情況下,只是用值初始化一下內部的那個x,執行y訪問的當然是自己作用域內的x了,也就是被變成了20的那個x。

//3. 函式體內聲明瞭與引數x同名的變數x,但這個x與引數x沒有任何關係,只存在一個初始化賦值的操作。因此此處的x是函式體內作用域的那個存30的x變數

//4. 全域性量就不說了,和上述兩個作用域都沒有關係,不會被改變。

 我們再看看下面的例子,有助於加深理解:有預設值的函式宣告作用域和函式作用域有什麼關係!

function f1( x=1,y=()=>{x=10;} ){
            y();
            console.log( x );
            
var x = 'abc'; } f1(); function f2( x=1,y=()=>{x=10;} ){ y(); console.log( x );//10 //var x = 'abc'; } f2();

執行結果分別如下:

分析如下:

f1中,因為引數有預設值,所以引數開闢了自己的作用域,函式體內聲明瞭自己的x,也開闢了自己的作用域,這倆x沒任何關係,只是引數用自己的值初始化了函式體內的x,所以它列印了1

f2中,函式體內未宣告自己的同名變數,在變數解析的時候就順著"作用域鏈"爬到了引數宣告作用域內,此時訪問的時候就是訪問的引數宣告作用域內的x。

綜上,得到Final conclusion:

1. 有預設值的函式,的確是開闢了單獨的“引數宣告作用域A”,這個作用域與函式體內的作用域是互不相干的,獨立的。

2. 函式體內的同名變數接受來自同名引數的預設賦值,並單獨開闢函式體內的一個作用域B.

3. 在作用域B中找不到變數時,程式會攀援“scope-chain”找到作用域A中的變數,並加以操作。

所以,在沒有let之前的作用域A和作用域B是互相獨立又有“上溯”查詢的關係,比較難以理解,雖然沒人在傳參後再宣告一個同名的區域性變數,但我們要理解js底層對這種行為的處理方式。

好在,ES6誕生了!let宣告重名變數會報錯,從根本上杜絕了這種現象的發生。

———學無止境,may stars guide your way