1. 函數聲明和函數表達式有什么區(qū)別?
函數聲明:function fn(){};
函數表達式:var fn=function(){};
區(qū)別: 1. 函數聲明構造的函數要有函數名稱染苛。函數表達式可以是匿名函數。
2. 函數聲明可以變量提升几缭,所以函數可以在聲明前調用。而函數表達式不可以沃呢。(如圖)
2. 什么是變量的聲明前置年栓?什么是函數的聲明前置?
- 變量聲明前置:所謂的變量聲明前置就是在一個作用域塊中,所有的變量都被放在塊的開始出聲明(如圖)
- 函數聲明前置:對于函數聲明薄霜,js解析器會優(yōu)先讀取某抓,確保在所有代碼執(zhí)行之前聲明已經被解析,而函數表達式惰瓜,如同定義其它基本類型的變量一樣否副,只在執(zhí)行到某一句時也會對其進行解析。(如圖)
3. arguments 是什么?
- arguments不能夠創(chuàng)建崎坊,是函數自身的參數备禀,只有當函數開始執(zhí)行是才能使用
雖然arguments的使用方法,很像數組奈揍,但是它并不是數組曲尸。
4 .函數的重載怎樣實現
JS的函數定義可以指定形式參數名稱,多多少少我們會以為js至少可以支持參數個數不同的方法重載男翰,然而遺憾的是這僅僅是一個假象另患,js所有的參數都是以arguments傳遞過去的,這個參數類似于數組奏篙,在函數調用的時候柴淘,所有的實參都是保存在了這個數據結構里面迫淹,我們定義函數的時候所指定的形式參數其實是為這個數據結構里面的數據定義一個快捷的訪問方式。也就是說js所有的函數都是支持無限個參數的为严,加上數據類型是弱類型敛熬,那么JS的函數除了名稱就真的沒有方法區(qū)別了?
辦法總是有的,我們可以利用JavaScript中的特殊對象arguments來模擬函數重載第股。用它來判斷傳入參數的個數或類型以區(qū)分
5. 立即執(zhí)行函數表達式是什么应民?有什么作用?
- 執(zhí)行函數表達式是什么
(function(){alert(‘我是匿名函數’)}()); // 用括號把整個表達式包起來
(function(){alert(‘我是匿名函數’)})(); //用括號把函數包起來
- 作用:
一是不必為函數命名,避免了污染全局變量夕吻;
二是函數內部形成了一個單獨的作用域诲锹,可以封裝一些外部無法讀取的私有變量。
6. 什么是函數的作用域鏈
作用域就是變量與函數的可訪問范圍涉馅,即作用域控制著變量與函數的可見性和生命周期归园。
在JavaScript中,變量的作用域有全局作用域和局部作用域兩種稚矿。
在代碼中任何地方都能訪問到的對象擁有全局作用域
局部作用域一般只在固定的代碼片段內可訪問到庸诱,最常見的例如函數內部,所有在一些地方也會看到有人把這種作用域稱為函數作用域
在JavaScript中晤揣,函數也是對象桥爽,實際上,JavaScript里一切都是對象昧识。函數對象和其它對象一樣钠四,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內部屬性。其中一個內部屬性是[[Scope]]跪楞,由ECMA-262標準第三版定義缀去,該內部屬性包含了函數被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數的作用域鏈习霹,它決定了哪些數據能被函數訪問朵耕。
-
舉例:
0_1481213643993_QQ截圖20161209001349.jpg
1.分析原因:
第一步:首先定義了一個全局變量a,并且將1賦值給這個a淋叶;
第二步:然后建立了一個函數fn()阎曹,在函數fn里面定義了局部變量b,并且賦值2給b煞檩;
第三步:求fn2()的值处嫌,并且定義了fn2()函數,里面是console.log(a)和console.log(c);
第四步:在函數fn中定義了局部變量c并賦值3給c斟湃;
第五步:求函數fn()熏迹;
那么,在這中間發(fā)生了什么呢凝赛?
1.變量提升注暗,全局變量a的聲明提升到最上面變成var = a坛缕;a = 1;
2.函數fn()中的局部變量b和c的變量提升到fn()的最上面變成var b捆昏;var c赚楚;
3.然后函數fn2提升到var b;var c骗卜;的下面宠页,后面接著就是求fn2();
4.變量的賦值在函數fn()的最底下:b = 2寇仓; c = 3举户;
5.求fn()的值,也就是fn2()的值遍烦;
6.執(zhí)行的順序是從上到下的俭嘁,整個函數變成了如下代碼
這個函數中的作用域鏈是這樣的:
1.從上訴可知這個函數的作用域有三個,互成鏈條服猪,分別是(從外到內)①:全局作用域兄淫;②:fn()作用域;③:fn2()作用域
2.執(zhí)行函數fn2()后蔓姚,首先在函數fn2()中console.log(a),fn2()就在自身域中找a慨丐,沒有找到就到上面一層也就是fn()中找a坡脐,也沒有找到,于是就到全局變量中找a房揭,得到a已經被聲明备闲,并且值為1,所以得到a為1捅暴;
3.然后fn2()中console.log(c)恬砂,c在fn2()作用域中也沒有找到,就到上一層fn()作用域中找c蓬痒,而c此時已經被聲明但是并未賦值泻骤,所以console.log(c)會得到undefined;
4.執(zhí)行完函數fn2()得出結果后梧奢,fn2()被銷毀狱掂,得出的值變?yōu)閒n()的值;
上面所訴的就是一個函數的作用域鏈亲轨,總結來說就是趋惨,某個函數尋求變量的時候,在自身作用域中找不到就會到上一層作用域去找惦蚊,一直到全局變量為止器虾,這一個個的作用域連接起來就是作用域鏈讯嫂;
代碼練習
1. 以下代碼輸出什么?
1兆沙、在函數體內部可以通過arguments對象來訪問傳遞給函數的每一個參數欧芽。
2、函數不介意傳遞進來多少個參數挤悉,也不在乎傳進來的參數是什么類型渐裸,如果沒有傳遞值的命名參數將自動被賦予undefined值。
3装悲、arguments的值永遠與對應命名的參數的值保持同步昏鹃。
2. 寫一個函數,返回參數的平方和诀诊?
3 .如下代碼的輸出洞渤?為什么 ?
變量提升:
var a;
console.log(a);
a=1;
console.log(b);
a被聲明但沒有賦值所以第一個undefined;
b沒有被聲明報錯;
4. 如下代碼的輸出属瓣?為什么
sayName函數聲明载迄,跟變量聲明提前一樣,也會進行函數聲明前置抡蛙。所以sayName的函數打印結果為hello world
saAge是函數表達式护昧,不會進行聲明提前,所以當調用表達式在函數表達式前面會報錯提示sayAge is not a function 粗截,如果調試表達式在函數表達式后面惋耙。就能正常使用。
5. 如下代碼的輸出熊昌?為什么
當在同一個作用域內定義了名字相同的變量和方法的話绽榛,無論其順序如何,變量的賦值會覆蓋方法的賦值.
6. 如下代碼的輸出婿屹?為什么
7. 如下代碼的輸出灭美?為什么?
輸出結果是錯誤昂利,fn不是一個函數
原因:fn被聲明并被賦值為1届腐,此時的fn不是一個函數,而console.log(fn(fn))是輸出這個fn這個函數蜂奸,所以發(fā)生沖突梯捕;
8 .如下代碼的輸出?為什么窝撵?
0_1481210792302_QQ截圖20161208232404.jpg
9. 如下代碼的輸出傀顾?為什么
10. 如下代碼的輸出?為什么
版權歸饑人谷--楠柒所有如有轉發(fā)請標明出處謝謝