1. 函數(shù)聲明和函數(shù)表達式有什么區(qū)別?
函數(shù)聲明:function fn(){};
函數(shù)表達式:var fn=function(){};
區(qū)別: 1. 函數(shù)聲明構(gòu)造的函數(shù)要有函數(shù)名稱。函數(shù)表達式可以是匿名函數(shù)。
2. 函數(shù)聲明可以變量提升,所以函數(shù)可以在聲明前調(diào)用。而函數(shù)表達式不可以。(如圖)

2. 什么是變量的聲明前置?什么是函數(shù)的聲明前置?
- 變量聲明前置:所謂的變量聲明前置就是在一個作用域塊中,所有的變量都被放在塊的開始出聲明(如圖)

- 函數(shù)聲明前置:對于函數(shù)聲明,js解析器會優(yōu)先讀取,確保在所有代碼執(zhí)行之前聲明已經(jīng)被解析,而函數(shù)表達式,如同定義其它基本類型的變量一樣,只在執(zhí)行到某一句時也會對其進行解析。(如圖)


3. arguments 是什么?
- arguments不能夠創(chuàng)建,是函數(shù)自身的參數(shù),只有當(dāng)函數(shù)開始執(zhí)行是才能使用
雖然arguments的使用方法,很像數(shù)組,但是它并不是數(shù)組。


4 .函數(shù)的重載怎樣實現(xiàn)
JS的函數(shù)定義可以指定形式參數(shù)名稱,多多少少我們會以為js至少可以支持參數(shù)個數(shù)不同的方法重載,然而遺憾的是這僅僅是一個假象,js所有的參數(shù)都是以arguments傳遞過去的,這個參數(shù)類似于數(shù)組,在函數(shù)調(diào)用的時候,所有的實參都是保存在了這個數(shù)據(jù)結(jié)構(gòu)里面,我們定義函數(shù)的時候所指定的形式參數(shù)其實是為這個數(shù)據(jù)結(jié)構(gòu)里面的數(shù)據(jù)定義一個快捷的訪問方式。也就是說js所有的函數(shù)都是支持無限個參數(shù)的,加上數(shù)據(jù)類型是弱類型,那么JS的函數(shù)除了名稱就真的沒有方法區(qū)別了?
辦法總是有的,我們可以利用JavaScript中的特殊對象arguments來模擬函數(shù)重載。用它來判斷傳入?yún)?shù)的個數(shù)或類型以區(qū)分

5. 立即執(zhí)行函數(shù)表達式是什么?有什么作用?
- 執(zhí)行函數(shù)表達式是什么
(function(){alert(‘我是匿名函數(shù)’)}()); // 用括號把整個表達式包起來
(function(){alert(‘我是匿名函數(shù)’)})(); //用括號把函數(shù)包起來
- 作用:
一是不必為函數(shù)命名,避免了污染全局變量;
二是函數(shù)內(nèi)部形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量。
6. 什么是函數(shù)的作用域鏈
作用域就是變量與函數(shù)的可訪問范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。
在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。
在代碼中任何地方都能訪問到的對象擁有全局作用域
局部作用域一般只在固定的代碼片段內(nèi)可訪問到,最常見的例如函數(shù)內(nèi)部,所有在一些地方也會看到有人把這種作用域稱為函數(shù)作用域
在JavaScript中,函數(shù)也是對象,實際上,JavaScript里一切都是對象。函數(shù)對象和其它對象一樣,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性。其中一個內(nèi)部屬性是[[Scope]],由ECMA-262標(biāo)準(zhǔn)第三版定義,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。
-
舉例:
0_1481213643993_QQ截圖20161209001349.jpg
1.分析原因:
第一步:首先定義了一個全局變量a,并且將1賦值給這個a;
第二步:然后建立了一個函數(shù)fn(),在函數(shù)fn里面定義了局部變量b,并且賦值2給b;
第三步:求fn2()的值,并且定義了fn2()函數(shù),里面是console.log(a)和console.log(c);
第四步:在函數(shù)fn中定義了局部變量c并賦值3給c;
第五步:求函數(shù)fn();
那么,在這中間發(fā)生了什么呢?
1.變量提升,全局變量a的聲明提升到最上面變成var = a;a = 1;
2.函數(shù)fn()中的局部變量b和c的變量提升到fn()的最上面變成var b;var c;
3.然后函數(shù)fn2提升到var b;var c;的下面,后面接著就是求fn2();
4.變量的賦值在函數(shù)fn()的最底下:b = 2; c = 3;
5.求fn()的值,也就是fn2()的值;
6.執(zhí)行的順序是從上到下的,整個函數(shù)變成了如下代碼

這個函數(shù)中的作用域鏈?zhǔn)沁@樣的:
1.從上訴可知這個函數(shù)的作用域有三個,互成鏈條,分別是(從外到內(nèi))①:全局作用域;②:fn()作用域;③:fn2()作用域
2.執(zhí)行函數(shù)fn2()后,首先在函數(shù)fn2()中console.log(a),fn2()就在自身域中找a,沒有找到就到上面一層也就是fn()中找a,也沒有找到,于是就到全局變量中找a,得到a已經(jīng)被聲明,并且值為1,所以得到a為1;
3.然后fn2()中console.log(c),c在fn2()作用域中也沒有找到,就到上一層fn()作用域中找c,而c此時已經(jīng)被聲明但是并未賦值,所以console.log(c)會得到undefined;
4.執(zhí)行完函數(shù)fn2()得出結(jié)果后,fn2()被銷毀,得出的值變?yōu)閒n()的值;
上面所訴的就是一個函數(shù)的作用域鏈,總結(jié)來說就是,某個函數(shù)尋求變量的時候,在自身作用域中找不到就會到上一層作用域去找,一直到全局變量為止,這一個個的作用域連接起來就是作用域鏈;
代碼練習(xí)
1. 以下代碼輸出什么?

1、在函數(shù)體內(nèi)部可以通過arguments對象來訪問傳遞給函數(shù)的每一個參數(shù)。
2、函數(shù)不介意傳遞進來多少個參數(shù),也不在乎傳進來的參數(shù)是什么類型,如果沒有傳遞值的命名參數(shù)將自動被賦予undefined值。
3、arguments的值永遠(yuǎn)與對應(yīng)命名的參數(shù)的值保持同步。
2. 寫一個函數(shù),返回參數(shù)的平方和?

3 .如下代碼的輸出?為什么 ?

變量提升:
var a;
console.log(a);
a=1;
console.log(b);
a被聲明但沒有賦值所以第一個undefined;
b沒有被聲明報錯;
4. 如下代碼的輸出?為什么

sayName函數(shù)聲明,跟變量聲明提前一樣,也會進行函數(shù)聲明前置。所以sayName的函數(shù)打印結(jié)果為hello world
saAge是函數(shù)表達式,不會進行聲明提前,所以當(dāng)調(diào)用表達式在函數(shù)表達式前面會報錯提示sayAge is not a function ,如果調(diào)試表達式在函數(shù)表達式后面。就能正常使用。
5. 如下代碼的輸出?為什么

當(dāng)在同一個作用域內(nèi)定義了名字相同的變量和方法的話,無論其順序如何,變量的賦值會覆蓋方法的賦值.
6. 如下代碼的輸出?為什么


7. 如下代碼的輸出?為什么?

輸出結(jié)果是錯誤,fn不是一個函數(shù)
原因:fn被聲明并被賦值為1,此時的fn不是一個函數(shù),而console.log(fn(fn))是輸出這個fn這個函數(shù),所以發(fā)生沖突;
8 .如下代碼的輸出?為什么?
0_1481210792302_QQ截圖20161208232404.jpg


9. 如下代碼的輸出?為什么


10. 如下代碼的輸出?為什么


版權(quán)歸饑人谷--楠柒所有如有轉(zhuǎn)發(fā)請標(biāo)明出處謝謝
