1.函數(shù)聲明和函數(shù)表達式有什么區(qū)別 ?
函數(shù)聲明:
function func(){
console.log(1)
}
函數(shù)表達式:(一般匿名函數(shù)賦給一個變量)
var tmp = function (){
console.log(1)
};
因此函數(shù)聲明和函數(shù)表達式不同之處在于:
1、js引擎在解析javascript代碼時會[函數(shù)聲明提升],把當前執(zhí)行環(huán)境(作用域)上的函數(shù)聲明給提到執(zhí)行環(huán)境的前頭;而函數(shù)表達式則必須是等到j(luò)s引擎執(zhí)行到它所在的那行代碼,才會從上而下一行一行地解析函數(shù)表達式中的代碼
2、函數(shù)聲明之后,只需要 func()即可調(diào)用;而函數(shù)表達式后面可以加括號作為立即執(zhí)行函數(shù)調(diào)用,函數(shù)聲明則不可以。
3、函數(shù)聲明是以function關(guān)鍵詞開始,如果不是,那么它就是函數(shù)表達式。
4、函數(shù)聲明最后面一般不寫分號,而函數(shù)表達式有分號。
2.什么是變量的聲明前置?什么是函數(shù)的聲明前置
js引擎的工作方式:先解析代碼,獲取所有被聲明的變量,然后一行一行執(zhí)行,會把所有的變量的聲明語句都被提到代碼的頭部,這就叫做變量的聲明前置。
- 變量的聲明前置:
①變量前置就是把變量的聲明提前到當前作用域的最前面,但變量的賦值仍然按照原來的順序執(zhí)行,如果變量聲明但未被賦值,變量會自動賦值為undefined。
var a = 2;
等同于
var a;
a = 2;
- 函數(shù)的聲明前置:
①函數(shù)聲明前置和變量的聲明前置實質(zhì)是一樣的。函數(shù)的聲明前置有兩種情況,一個是使用函數(shù)聲明,則整個聲明都前置,而且會被前置到變量聲明的后面;另一個是使用函數(shù)表達式,那么規(guī)則和變量的聲明前置一樣。
fn()
function fn(){
console.log(1)
}//1
等同于
function fn(){ console.log (1) }
fn()
- 函數(shù)表達式的聲明前置:
fn(1)
var f = function fn(n){
console.log(n)
}
等同于
var f;
fn(1)
f = function fn(n){
console.log(n)
}
//Uncaught ReferenceError: fn is not defined(…)
疑問:寫上面這題的函數(shù)表達式用具名函數(shù)還是匿名函數(shù)規(guī)范呢?
3.arguments 是什么
- 在函數(shù)內(nèi)部,可以使用arguments對象獲取到該函數(shù)傳入的全部參數(shù),一般簡稱為[參數(shù)列表]
(1).arguments是一個類數(shù)組對象,代表傳給一個function的參數(shù)列表,只在函數(shù)內(nèi)部起作用。arguments的值與函數(shù)傳入實參有關(guān),與函數(shù)定義形參無關(guān)。
(2).即使函數(shù)聲明時不用詳細寫清楚傳入的參數(shù)的個數(shù)和名稱,也能使用arguments獲取參數(shù)。
(3).arguments是一個偽數(shù)組,無真正數(shù)組的API(eg:push、pop那些方法)。
(4).arguments.length:指的是實參的長度,不是形參的長度。
(5).arguments是function的隱含屬性,它將會把實參顯示成一個類組對象。
function fn(a,b,c){
console.log(arguments)
console.log(arguments.length)
}
fn(1,2,3,4)

4.函數(shù)的重載怎樣實現(xiàn)
①重載是很多面向?qū)ο笳Z言實現(xiàn)多態(tài)的手段之一,相同名字的函數(shù)參數(shù)個數(shù)不同或者順序不同都被認為是不同的函數(shù),稱為函數(shù)重載。
②在JavaScript中沒有函數(shù)重載的概念,函數(shù)通過名字確定唯一性,參數(shù)不同也被認為是相同的函數(shù),后面的覆蓋前面的。
那是否意味著JS不能通過重載功能來實現(xiàn)一個函數(shù),參數(shù)不同功能不同呢?
答:JavaScript可以通過自身的屬性去模擬函數(shù)重載;可以用arguments來實現(xiàn)函數(shù)的重載(關(guān)鍵只要傳實參的個數(shù)有所不同即可)

5.立即執(zhí)行函數(shù)表達式是什么?有什么作用
立即執(zhí)行函數(shù)表達式(Immediately-Invoked Function Expression)簡稱IIFE
通俗點解釋:平時寫語句是無需得出結(jié)果,但是表達式是需要求出結(jié)果的。所以推測出立即執(zhí)行函數(shù)表達式字面意思是一個為了馬上得出結(jié)果的函數(shù)。
- 立即執(zhí)行函數(shù)表達式是什么?
(1).聲明一個匿名函數(shù)
(2).馬上調(diào)用這個匿名函數(shù)
(3).聲明一個匿名函數(shù)function(){} ,然后在匿名函數(shù)后面加一對括號() ,調(diào)用這個匿名函數(shù)
(4).在function前面加!、 +、 — 、()可以起到函數(shù)定義后立即執(zhí)行的效果,加上運算符后,就告訴js引擎這是一個函數(shù)表達式,不是函數(shù)定義。
典型一:(function(){ alert("我是匿名函數(shù)") })()
典型二:(function(){ alert("我是匿名函數(shù)") }())
典型三:!function (){ alert("我是匿名函數(shù)") }()
典型四:var fn =function(n){console.log(n)}(1) //1
- 立即執(zhí)行函數(shù)表達式有什么作用?
作用:創(chuàng)建一個獨立的作用域;這個作用域里面的變量,外面訪問不到(即避免[變量污染]);
當同時調(diào)用多個JS的情況時,很容易造成因為變量名一致或函數(shù)名一致而覆蓋其他庫的變量;所以才需要采用立即執(zhí)行函數(shù);
定義的變量都成了立即執(zhí)行函數(shù)的局部變量,以后即使變量名或函數(shù)名一樣也不被污染,同時形成了一個單獨的作用域,可以封裝一些外部無法讀取的私有變量;
6.什么是函數(shù)的作用域鏈
①作用域:作用域就是變量與函數(shù)的可訪問范圍,作用域控制著變量與函數(shù)的可見性和生命周期。
- 全局變量:變量沒有在函數(shù)內(nèi)聲明或者聲明的時候沒有帶var就是全局變量,擁有全局作用域;
- 局部變量:函數(shù)內(nèi)部聲明并且以var修飾的變量就是局部變量,只能在函數(shù)體內(nèi)使用,函數(shù)的參數(shù)雖然沒有使用var但仍然是局部變量。
②注意:JavaScript并沒有塊級作用域,只有函數(shù)作用域:變量在聲明它們的函數(shù)體及其子函數(shù)內(nèi)是可見的。
③作用域鏈:
作用域鏈用來變量查詢的,在代碼執(zhí)行查找過程中,變量會在當前作用域中進行尋找,如果找不到,就會往沿著作用域鏈向上一級進行尋找,一直到全局作用域為止,如果找到便會停止(而不理會上一級是否有同名的變量),如果找不到,就會報錯。
④作用域只會逐層向上查找,遵循就近原則,只要查找到就停止了。

代碼:
1.以下代碼輸出什么?

2.寫一個函數(shù),返回參數(shù)的平方和?
//題目:寫一個函數(shù),返回參數(shù)的平方和
// function sumOfSquares(){
// }
// sumOfSquares(2,3,4); // 29
// sumOfSquares(1,3); // 10
--------
方法1:
function sumOfSquares(){
var result = 0
for(var i=0; i<arguments.length; i++){
result += arguments[i]*arguments[i]
}
return result
}
console.log( sumOfSquares(2,3,4) ); // 29
console.log( sumOfSquares(1,3) ); // 10
-----------
方法2:
function sumOfSquares(){
var result = 0
for(var i=0; i<arguments.length; i++){
result += arguments[i]*arguments[i]
}
console.log(result)
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
3.如下代碼的輸出?為什么
console.log(a);//打印為undefined,定義了但未賦值
var a = 1;
console.log(b);//b is not defined,b沒有聲明,所以直接報錯
4.如下代碼的輸出?為什么
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
等同于
var sayAge
function sayName(name){ //定義時不看函數(shù)體內(nèi)容
console.log('hello ', name);
}
sayName('world') // 輸出:hello world
sayAge(10) //輸出:sayAge is a not function ;相當于undefined(10),肯定會報錯
sayAge = function(age){
console.log(age);
};
5.如下代碼的輸出?為什么
function fn(){}
var fn = 3;
console.log(fn);
等同于
var fn; //變量的聲明前置
function fn(){}//聲明提升,函數(shù)聲明在變量的后面,函數(shù)覆蓋變量
fn = 3; //變量的賦值覆蓋函數(shù)的聲明
console,log(fn) //輸出3
6,如下代碼的輸出?為什么
function fn(fn2){
console.log(fn2); //函數(shù)fn2
var fn2 = 3;
console.log(fn2); //3
console.log(fn); //外層fn函數(shù)
function fn2(){
console.log('fnnn2');
}
}
fn(10);
等同于
function fn(fn2){
var fn2;
//注意:傳參數(shù)時隱式做了 var fn2 = 10
function fn2(){console.log('fnnn2')}; //函數(shù)聲明前置覆蓋了之前的變量
console.log(fn2);// 輸出上面的fn2(){}函數(shù)
fn2 = 3;
console.log(fn2); //3
console.log(fn); //輸出最外層的fn函數(shù),當前作用域(函數(shù)fn內(nèi)部)中沒有這個變量,因此向上一級尋找,找到變量fn被聲明為函數(shù)
}
fn(10)
分析:
1.當輸入 fn(10)的時候,把fn2 直接賦值成了數(shù)值10;
2.剛進入函數(shù)體內(nèi)部時,var fn2; 這時候 fn2還是等于10;
3.接著被賦值成數(shù)值的fn2,又被聲明回函數(shù);
4.這個時候console.log(fn2);,那就輸出函數(shù)fn2;
5.然后又被賦值成數(shù)值3;
6.遇到console.log(fn2);輸出3
7.然后遇到console.log(fn),輸出函數(shù) fn();
7.如下代碼的輸出?為什么
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
等同于
var fn;
function fn(fn){
console.log(fn)
}
fn = 1;
console.log(fn(fn)); //居然是報錯!is not a function
第七題解析:因為當函數(shù)和變量聲明前置,函數(shù)的聲明覆蓋了變量的聲明,最后1賦值給fn,這時候fu類型不是function而是number,這時候console.log(fn(fn)) 轉(zhuǎn)換就變成console.log(1(1)) ,所以console.log(fn(fn)) 就報錯。顯示fn不是一個函數(shù)。
ps:請老師解析下,這題好懸,我是在控制臺打出才得知會報錯
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
-------
等同于
var i;
var j;
console.log(j);
console.log(i);
i=0;
for(i<10;i++){//只有函數(shù)才有作用域,for循環(huán)沒有作用域,所以里面的變量是全局變量
j = 100
}
console.log(i)
console.log(j)
for循環(huán)后i跳出就變成10;循環(huán)10次后j被賦值了依然還是100,變量不會隨著循環(huán)的結(jié)束而消失
9.如下代碼的輸出?為什么
fn();
var i = 10;
var fn = 20;
console.log(i);
function fn(){
console.log(i);
var i = 99;
fn2();
console.log(i);
function fn2(){
i = 100;
}
}
----------
等同于
var i; //變量提升
var fn;
function fn(){ //函數(shù)聲明提升
var i
function fn2(){ //函數(shù)定義但未執(zhí)行
i = 100
}
console.log(i) //undefined
i = 99
fn2()
console.log(i) //100 fn2函數(shù)運行后,本來全局的i=99被fn2的i=100覆蓋了
}
fn(); //然后執(zhí)行函數(shù)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);
------
等同于
var say;
(function say(n){ //立即執(zhí)行函數(shù)
console.log(n); //輸出:10 9 8 7 6 5 4 3 2
if(n<3) return;
say(n-1);
}( 10 ));
say = 0
console.log(say);//輸出:0
//變量只在函數(shù)作用域內(nèi)生效,并不會影響到外面的變量。所以最后console.log(say)輸出0
分析:
1.聲明var say = 0;
2.立即執(zhí)行函數(shù)表達式不會提升的,按位置立即執(zhí)行;
3.進入函數(shù),實參10賦給形參n,n = 10,首先打出console.log(10) ;
4.判斷語句if n<3 return,不滿足條件,執(zhí)行say(n-1);繼續(xù)輸出n,一直遞歸下去 ;
5.直到n=2的時候,n<3此時返回,結(jié)束循環(huán),return為空值的時候,就是return出undefined,函數(shù)執(zhí)行完畢;
6.console.log(say)輸出0 ,因為立即執(zhí)行函數(shù)表達式就是創(chuàng)建一個獨立的作用域;這個作用域里面的變量,外面是訪問不到,所以打出全局定義的變量say;
本文版權(quán)歸區(qū)子銘和饑人谷所有,轉(zhuǎn)載請注明來源。