1.函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別 (*)
函數(shù)聲明和函數(shù)表達(dá)式都可以創(chuàng)建函數(shù)。
函數(shù)聲明寫法:
function 函數(shù)名稱 (參數(shù):可選) {函數(shù)體 }
舉例:
function foo() {}
函數(shù)表達(dá)式寫法:
var 變量 = function 函數(shù)名稱(可選) (參數(shù):可選) {函數(shù)體 }
舉例:
var foo=function(){};
這種寫法將一個(gè)匿名函數(shù)賦值給變量。這時(shí),這個(gè)匿名函數(shù)又稱函數(shù)表達(dá)式(Function Expression),因?yàn)橘x值語句的等號右側(cè)只能放表達(dá)式。
采用函數(shù)表達(dá)式聲明函數(shù)時(shí),function命令后面不帶有函數(shù)名。如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無效。

二者區(qū)別主要在于,函數(shù)聲明有聲明提前的特點(diǎn),函數(shù)聲明會被提到被提升到作用域的最前面,即使寫代碼的時(shí)候是寫在最后面,也還是會被提升至最前面。
舉例:

這段代碼的實(shí)際執(zhí)行順序是:
var btn;
function fo(){}
console.log(fo);
console.log(btn);
btn=function(){};
所以輸出結(jié)果,是function fo(){} undefined。

2.什么是變量的聲明前置?什么是函數(shù)的聲明前置 ?(**)
變量的聲明前置,把所有變量聲明放到代碼的頭部。
函數(shù)的聲明前置是把函數(shù)聲明放到代碼的頭部。如果我們使用函數(shù)聲明的方式,那么即使函數(shù)寫在最后也可以在前面語句調(diào)用,前提是函數(shù)聲明部分已經(jīng)被下載到本地。
舉例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>聲明提前</title>
</head>
<body>
<script>
console.log(a);
function fn(){
console.log(b);
console.log(foo);
var b=4;
function foo(){}
}
var a = 9;
fn();
</script>
</body>
</html>
結(jié)果:

這就是變量的聲明前置和函數(shù)的聲明前置的結(jié)果。
3.arguments 是什么 (*)
arguments 是一個(gè)類數(shù)組對象。代表傳給一個(gè)function的參數(shù)列表。arguments 對象是函數(shù)內(nèi)部的本地變量;arguments 已經(jīng)不再是函數(shù)的屬性了。
在函數(shù)內(nèi)部可以用arguments對象來獲取函數(shù)的所有參數(shù)。而且arguments對象僅在函數(shù)內(nèi)部有效,在函數(shù)外部調(diào)用 arguments 對象會出現(xiàn)一個(gè)錯(cuò)誤。
它的作用有:
1.無須明確指出參數(shù)名,可以訪問函數(shù)。

2.無需明確命名參數(shù),就可以重寫函數(shù)。

可以看到name的值被改變了。
3.可以用 arguments 對象檢測函數(shù)的參數(shù)個(gè)數(shù),引用屬性 arguments.length 即可。

可以看到實(shí)際函數(shù)調(diào)用了幾個(gè)參數(shù),arguments.length就等于幾。
4.用arguments 對象判斷傳遞給函數(shù)的參數(shù)個(gè)數(shù),可以模擬函數(shù)重載。

當(dāng)只有一個(gè)參數(shù)時(shí),doAdd() 函數(shù)給參數(shù)加 5。如果有兩個(gè)參數(shù),則會把兩個(gè)參數(shù)相加,返回它們的和。所以,doAdd(10) 輸出的是 "15",而 doAdd(40, 20) 輸出的是 "60"。雖然不如重載那么好,不過已足以避開 ECMAScript 的這種限制。
4.函數(shù)的重載怎樣實(shí)現(xiàn) (**)
從語言角度來說,javascript不支持函數(shù)重載,先定義的函數(shù)會被后定義的函數(shù)覆蓋。不能夠定義同樣的函數(shù)然后通過編譯器去根據(jù)不同的參數(shù)執(zhí)行不同的函數(shù)。但是javascript卻可以通過自身屬性去模擬函數(shù)重載。如上題中介紹的arguments的第四個(gè)特性,就可以模擬函數(shù)重載。
參考 js函數(shù)重載
5.立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用 (***)
在Javascript中,一對圓括號()是一種運(yùn)算符,跟在函數(shù)名之后,表示調(diào)用該函數(shù)。比如,foo()就表示調(diào)用foo函數(shù)。
所以我們也可以在函數(shù)聲明后加上(),表示立即執(zhí)行函數(shù)。但是不能直接加括號,function(){}();會引起語法錯(cuò)誤。這是因?yàn)?,JavaScript引擎規(guī)定,如果function關(guān)鍵字出現(xiàn)在行首,一律解釋成語句。因此,JavaScript引擎看到行首是function關(guān)鍵字之后,認(rèn)為這一段都是函數(shù)的定義,不應(yīng)該以圓括號結(jié)尾,所以就報(bào)錯(cuò)了。
立即執(zhí)行函數(shù)可以寫成下面的形式:
(function(){ /*code */ }());
或者
(function(){/*code*/})();
上面兩種寫法都是以圓括號開頭,引擎就會認(rèn)為后面跟的是一個(gè)表示式,而不是函數(shù)定義語句,所以就避免了錯(cuò)誤。這就叫做“立即調(diào)用的函數(shù)表達(dá)式”(Immediately-Invoked Function Expression),簡稱IIFE。
它的目的有兩個(gè):
一是不必為函數(shù)命名,避免了污染全局變量;
二是IIFE內(nèi)部形成了一個(gè)單獨(dú)的作用域,可以封裝一些外部無法讀取的私有變量。
// 寫法一
var tmp =newData;
processData(tmp);
storeData(tmp);
// 寫法二
(function (){
var tmp = newData;
processData(tmp);
storeData(tmp);}());
上面代碼中,寫法二比寫法一更好,因?yàn)橥耆苊饬宋廴救肿兞俊?/p>
6.什么是函數(shù)的作用域鏈 (****)
- 函數(shù)作用域就是函數(shù)的可訪問范圍,簡單的說,一個(gè)function從
{開始,到}結(jié)束,是這個(gè)函數(shù)的作用域。不同的函數(shù)有不同的作用域。作用域中聲明的變量無法被作用域外部所訪問。但是作用域中可以訪問上級作用域中的變量。 - 作用域鏈正是內(nèi)部上下文所有變量對象(包括父變量對象)的列表,用來變量查詢。在代碼執(zhí)行的過程中,所用到的變量會在當(dāng)前作用域中進(jìn)行尋找,如果找不到,就會往沿著作用域鏈向上一級進(jìn)行尋找,一直到全局作用域?yàn)橹梗绻业奖銜V梗ǘ焕頃弦患壥欠裼型淖兞浚?,如果找不到,就會?bào)錯(cuò)。
1.參考JavaScript 開發(fā)進(jìn)階:理解 JavaScript 作用域和作用域鏈
2.參考深入理解JavaScript系列(14):作用域鏈(Scope Chain)
代碼題
1.以下代碼輸出什么? (難度**)
function getInfo(name, age, sex){
console.log('name:',name);
console.log('age:', age);
console.log('sex:', sex);
console.log(arguments);
arguments[0] = 'valley';
console.log('name', name);
}
getInfo('hunger', 28, '男');
/*name:hunger
age:28
sex:男
["hunger",28,"男"]
name valley*/
getInfo('hunger', 28);
/*name:hunger
age:28
sex:undefined
["hunger",28]
name valley*/
getInfo('男');
/*name:男
age:undefined
sex:undefined
["男"]
name valley*/
結(jié)果:

2.寫一個(gè)函數(shù),返回參數(shù)的平方和?如 (難度**)
function sumOfSquares(){
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>函數(shù)平方和</title>
</head>
<body>
<script>
function sumOfSquares(){
var s=0;
for(var i=0;i<arguments.length;i++){
s= arguments[i]*arguments[i]+s;
}
alert(s);
}
sumOfSquares(2,3,4);
sumOfSquares(1,3);
</script>
</body>
</html>
3.如下代碼的輸出?為什么 (難度*)
console.log(a);//undefined
var a = 1;
console.log(b);//not defined
結(jié)果:

因?yàn)樽兞刻嵘?,把對a的聲明提到前面,但是并沒有定義a,所以輸出undefined。而b則未被聲明,輸出錯(cuò)誤。
4.如下代碼的輸出?為什么 (難度*)
sayName('world');//hello world
sayAge(10); //not a function
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
結(jié)果:

函數(shù)聲明提前到代碼最前面,調(diào)用函數(shù)sayName。而
var sayAge = function(age){console.log(age);};定義了函數(shù)表達(dá)式,sayAge是一個(gè)變量,調(diào)用sayAge(10)會出錯(cuò)。
5.如下代碼的輸出?為什么 (難度**)
function fn(){}
var fn = 3;
console.log(fn);//3
結(jié)果:

當(dāng)在同一個(gè)作用域內(nèi)定義了名字相同的變量和方法的話,無論其順序如何,變量的賦值會覆蓋方法的賦值。
6.如下代碼的輸出?為什么 (難度***)
function fn(fn2){
var fn2
function fn2(){
console.log('fnnn2');
}
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
}
fn(10);
結(jié)果:

執(zhí)行順序:
function fn(fn2){
var fn2;
function fn2(){
console.log('fnnn2');
}
console.log(fn2);/*function fn2(){
console.log('fnnn2');
}*/
fn2 = 3;
console.log(fn2);//3
console.log(fn);
}
fn(10);
調(diào)用函數(shù),才會執(zhí)行函數(shù)中的內(nèi)容。console.log(fn);調(diào)用上級函數(shù)。
7.如下代碼的輸出?為什么 (難度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn)); //not a function
結(jié)果:

執(zhí)行順序:
var fn;
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn));
fn被賦值為1,是一個(gè)數(shù)字不是函數(shù),出錯(cuò)。
8.如下代碼的輸出?為什么 (難度**)
//作用域
console.log(j);//undefined
console.log(i);//undefined
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);//10
console.log(j);//100
結(jié)果:

for循環(huán)把i增加到10,j的值被設(shè)置為100。這兩個(gè)都是全局變量。
類似于:
console.log(j);
console.log(i);
var i=10;
var j=100;
console.log(i);
console.log(j);
輸出結(jié)果如上圖示。
9.如下代碼的輸出?為什么 (難度****)
fn();//undefined 100
var i = 10;
var fn = 20;
console.log(i);//10
function fn(){
console.log(i);//undefined
var i = 99;
fn2();
console.log(i);//100
function fn2(){
i = 100;
}
}
結(jié)果:

根據(jù)變量的聲明提升和函數(shù)的聲明提升,可以把上面的代碼寫成:
var i;
var fn;
function fn(){
var i;
function fn2(){
i = 100;
}//并沒有被調(diào)用,沒有運(yùn)行。
console.log(i);//undefined
i = 99;
fn2();//i=100,i是全局變量
console.log(i);//100
}
fn();
i=10;//定義i是10
fn=20;
console.log(i);//10
10.如下代碼的輸出?為什么 (難度*****)
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);//10 9 8 7 6 5 4 3 2 0
結(jié)果:

(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
是立即執(zhí)行函數(shù),這個(gè)函數(shù)是對n從10依次遞減,直到n<3,即n的值是2時(shí),停止執(zhí)行這個(gè)函數(shù)。得到數(shù)字10 9 8 7 6 5 4 3 2。而say又被賦值為0,最后輸出0。
本文版權(quán)歸本人和饑人谷所有,轉(zhuǎn)載請注明出處