一.初步了解函數(shù)
1.函數(shù)的定義和調(diào)用
1.1 函數(shù)定義
函數(shù)其實就是將多條語句組合成一個“語句軍團(tuán)”,集體作戰(zhàn)
//定義一個函數(shù),函數(shù)就是一組語句的集合
function fun(){
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
//函數(shù)調(diào)用
fun();
定義一個函數(shù),用關(guān)鍵字function來定義
function后面有一個空格,后面就是函數(shù)名字,函數(shù)的 名字也是標(biāo)識符,既然是標(biāo)識符,那么命名規(guī)范和變量命名就是一樣的。
定義一個函數(shù)也叫聲明一個函數(shù)
function 函數(shù)名(){
}
函數(shù)如果不調(diào)用,那么里面的語句就不會執(zhí)行,不調(diào)用就等于白寫。
1.2 函數(shù)調(diào)用
調(diào)用一i個函數(shù)的方法非常簡單,函數(shù)名后面加一個(),
( ) 是運算符, 表示執(zhí)行一個函數(shù)
執(zhí)行函數(shù):
函數(shù)名()
一旦調(diào)用了函數(shù),函數(shù)內(nèi)部的語句就會執(zhí)行
2.函數(shù)的參數(shù)
2.1 參數(shù)的了解
可以通過參數(shù)讓函數(shù)內(nèi)部的語句有所不同
定義函數(shù)的時候,內(nèi)部語句可能有一些懸而未決的量,就是變量,這些變量,我們要求在定義的時候羅列在小括號中:
比如:
function fun(num){
console.log("這已經(jīng)是我第” + num + "此說我愛你!");
}
調(diào)用的時候,要把這個變量的真實的值,一起寫在括號里,這樣隨著函數(shù)的調(diào)用,這個值也傳給了num';這就是參數(shù)傳遞
執(zhí)行這個函數(shù):
fun(99);
羅列在function小括號中的參數(shù),叫做;
調(diào)用時傳遞的值,叫做
形式參數(shù)就像占位置,先把位置站好,等你來賦值
2.2 參數(shù)個數(shù)
參數(shù)可以有無數(shù)個,用逗號隔開。
//有多少形式參數(shù)都可以,都羅列出來
function fun( a, b ){
console.log( a + b);
}
fun( 2,6); //輸出8
fun( 6,18); //輸出24
定義函數(shù)的時候,不需要指定類型:
function sum( a,b){
console.log( a + b);
也就是說調(diào)用的時候,傳進(jìn)去的值是什么類型,就是形參a、b就是什么類型
sum( "5" , 20);
輸出520 ,做的時連字符的運算
2.3 實參和形參個數(shù)不等
聲明函數(shù)的時候和調(diào)用函數(shù)的時候參數(shù)個數(shù)可以不一樣,也不報錯。
- 實參個數(shù)小于形參個數(shù)
sum( 20 ); // NaN
以為只傳了一個參數(shù),b就沒有傳遞,b被隱式的var了,所以值時undefined。
10+undefined就是NaN
也就是說沒有實參傳遞二點形參就是undefined。
- 實參個數(shù)大于形參個數(shù)
sum( 10,20,30,40,50); //30
只有前兩個參數(shù)被形參接受了,后面的參數(shù)無視了,
形參會按順序接收實參,
2.4 arguments
每一個函數(shù)里面都有一個隱式的arguments,這個時系統(tǒng)已經(jīng)給你創(chuàng)建好的,每個函數(shù)內(nèi)部都有,arguments是類似于數(shù)組的類數(shù)組,就是實參列表
類數(shù)組本質(zhì)上是一個對象,涵蓋了所有的實參
調(diào)用函數(shù)的時候,比如:
fun(45,56,346,335,231);
此時函數(shù)內(nèi)部,arguments就有一個下標(biāo),就依此等于上面調(diào)用的數(shù):
arguments[0] //45
arguments[1] //56
arguments[2] //346
arguments[3] //335
arguments[4] //231
.....
如果函數(shù)里面有形式參數(shù)列表,那么是和arguments同步的:
function fun(a,b){
arguments[0] = 8;//改變第一個參數(shù)值
alert(a); // 8 ,彈出改變后的值
console.log( arguments.length); //實參的長度
console.log( fun.length); //形參的 長度
fun(45,56,346,335,231);
arguments的功能是模擬函數(shù)的重載,使得同一個函數(shù),根據(jù)參數(shù)個數(shù)額不同,有不同的作用
4. 返回值
函數(shù)可以通過參數(shù)來接收東西,可以通過return的語句來返回值
function sum(a,b){
return a + b;//現(xiàn)在這個函數(shù)的返回值就是a+b的和
}
console.log( sum(5,4));//sum沒有輸出功能,就要用console.log輸出
//sum(5,4)實際上就成為了一個表達(dá)式,用來計算結(jié)果
//計算結(jié)果就是9,相當(dāng)于console.log(9);
函數(shù)只能有唯一的return,有if語句除外,因為if語句是分支語句
程序遇到了return,將立即返回結(jié)果,返回調(diào)用的地方,而函數(shù)內(nèi)return后面的語句將不再執(zhí)行
function fun(){
console.log(1);
console.log(2);
return; //返回一個空值
console.log(3);//這行語句不執(zhí)行,因為函數(shù)已經(jīng)return了所以不會打印3
}
fun(); // 1 ,2
程序是先執(zhí)行內(nèi)層在執(zhí)行外層。
函數(shù)可以接收很多值,返回一個值
函數(shù)的意義:
- 在出現(xiàn)大量程序相同的時候,可以封裝為一個function,這樣只要寫一次代碼就可以調(diào)用很多次,減少代碼耦合
- 在調(diào)用一個函數(shù)的時候,不用關(guān)心函數(shù)內(nèi)部的實現(xiàn)細(xì)節(jié),只要可以運用,并能給我們開發(fā)帶來好處
- 模塊化編程,然復(fù)雜的邏輯變得簡單
4. 遞歸
函數(shù)內(nèi)部又調(diào)用了函數(shù)自身,我們把這種情況叫做遞歸
斐波那契數(shù)列就是經(jīng)典的遞歸算法:
1、1、2、3、5、8、13、21、34、55、89、144、233……
輸出斐波那契數(shù)列
//1、1、2、3、5、8、13、21、34、55、89、144、233……
//fib(n) 就能得到第n位的數(shù)字
//fib(2) = 1
//fib(5) = 5
//fib(6) = 8
//fib(10) = 55
function fib(n){
if( n == 1 || n == 2){
return 1;
}else{
return fib( n - 1) + fib( n - 2);
}
}
//一點一點計算
for( var i =1; <= 55 ; i++){
console.log(fib(i));
}
二.函數(shù)表達(dá)式
定義函數(shù)處理使用function之外,還有一種方法,就是函數(shù)表達(dá)式。就是函數(shù)沒有名字,稱為“匿名函數(shù)”,為了能夠調(diào)用它,我們把這個匿名函數(shù),直接賦值給一個變量
var sum = function(a,b){
return a + b;
}
想調(diào)用這個函數(shù)的時候,就可以直接使用sum變量來調(diào)用。
console.log( sum(3,6) );
如果現(xiàn)在這個表達(dá)式中的function不是匿名函數(shù),而是有名字的:
var sum =function fun(a,b){
return a + b;
}
那么JS表現(xiàn)非常的奇怪,在外部只能用sum()來調(diào)用,fun()會引發(fā)錯誤!
也就是說,JS這個奇怪的特性,給我們提了個醒,定義函數(shù),只能用這兩種方法中的其一,不建議雜糅:
第一種,通過函數(shù)聲明定義函數(shù)
function sum(){
}
第二種,通過匿名函數(shù)的賦值定義函數(shù)
var sum = function(){
}
建議不要混用,這么寫也不會錯
var xixi = function haha(){
}
三.函數(shù)聲明的提升
JS在執(zhí)行前,會有一個解析的過程,會把所有的函數(shù)聲明,都提升到了最最開頭,然后再執(zhí)行第一行語句
所以,function定義在哪里,都不重要,程序總能找到這個函數(shù)
//先調(diào)用
fun();
//然后定義
function fun(){
console.log("我是函數(shù),我執(zhí)行了");
}
不會引發(fā)錯誤,打印能正常執(zhí)行。
3.1. 聲明函數(shù)和函數(shù)表達(dá)式提升方式不同
函數(shù)聲明會被提升,但函數(shù)表達(dá)式卻不會提升
函數(shù)表達(dá)式提升的是變量,變量提升后并不是一個函數(shù),所以在表達(dá)式之前執(zhí)行,會報錯,為類型錯誤,因為不是函數(shù)
fun() ;//報錯
var fun = function(){
alert("我是函數(shù),我執(zhí)行了");
}
這提了個醒,沒有極特殊的理由,都要使用function 關(guān)鍵字來定義函數(shù),而不要使用函數(shù)表達(dá)式來定義函數(shù).
3.1.1 函數(shù)優(yōu)先
sum();//現(xiàn)在這個sum到底是函數(shù)還是變量8呢
//函數(shù)優(yōu)先,遇到同名標(biāo)識符,預(yù)解析階段一定把這個標(biāo)識符給函數(shù)
var sum = 8;//定義一個變量,是8
function sum(){
alert("我是sum函數(shù),我執(zhí)行了");
}
面試很容易靠,就常見的面試題:
foo();
var foo;
function foo(){
console.log(1);
}
foo = function(){
console.log(2)
}
函數(shù)優(yōu)先,
現(xiàn)在foo這個標(biāo)識符沖突了,一個函數(shù)叫做foo,一個變量也叫做foo,預(yù)解析階段,如果遇見標(biāo)識符沖突,這個標(biāo)識符給函數(shù)
四.IIFE
IIFE就是immediately-invoked function expression,即時調(diào)用函數(shù)表達(dá)式
如果一個函數(shù),在定義的時候我們就直接想調(diào)用它,就是一個IIFE
視圖在定義函數(shù)的后面,直接寫圓括號:
function fun(){
alert("hello ");
}();
控制臺報錯,這是因為函數(shù)是一個函數(shù)體,并不是表達(dá)式,只有表達(dá)式能夠用()來執(zhí)行。
所以就要把function fun(){}“降級”,從函數(shù)體降級為表達(dá)式。
+function fun(){
alert("hello");
}();
-function fun(){
alert("hello");
}();
更通常更常用的:
(function fun(){
alert("hello");
})();
用這種方法定義的函數(shù),名字是無效的,其他的地方是調(diào)用這個函數(shù):
fun();
所以IIFE里面的函數(shù),都是匿名函數(shù):
(function(){
alert("哈哈");
})();
上面就是一個標(biāo)準(zhǔn)的IIFE.