函數(shù)的類型:
以上的幾種方法創(chuàng)建的函數(shù),除了匿名函數(shù)外,其他的都屬于普通函數(shù),但是JS還有幾種特殊的的函數(shù)類型
回調(diào)函數(shù):
將函數(shù)當(dāng)做實(shí)參傳到另一個(gè)函數(shù)中,在另一個(gè)函數(shù)內(nèi)部調(diào)用,稱為回調(diào)函數(shù)。
//創(chuàng)建一個(gè)函數(shù),并設(shè)置型參callbakc用來接收函數(shù)
function fun(callback){
//在函數(shù)內(nèi)部調(diào)用接收到的函數(shù)
callback();
}
//調(diào)用個(gè)函數(shù),并傳入另一個(gè)函數(shù),且不限制類型,可以是匿名函數(shù)
fun(function (){
console.log("我是回調(diào)函數(shù)");
});
既然傳入的實(shí)參也是函數(shù),那么它也遵守函數(shù)的一切原則,且擁有函數(shù)的一切功能,同樣可以傳入實(shí)參:
//設(shè)置一個(gè)函數(shù),并設(shè)置三個(gè)型參用來接收回調(diào)函數(shù)和其他的兩個(gè)參數(shù)
function fn(myName,sex,callback){
//調(diào)用傳入的函數(shù),并設(shè)置兩個(gè)實(shí)參,將設(shè)置的型參作為實(shí)參傳進(jìn)去,
callback(myName,sex);
}
//調(diào)用函數(shù),并設(shè)置實(shí)參,其中有將一個(gè)匿名函數(shù)作為實(shí)參傳入
fn('小蘭','女',function(myName,sex){
//傳入的其他兩個(gè)實(shí)參會(huì)傳給fn的型參然后作為實(shí)參傳入匿名函數(shù)
console.log('你好,我是' + myName + ',性別:' + sex)
});
注意: 函數(shù)和變量一樣,都擁有聲明提前的特性,所以調(diào)用不管是寫在前面還是寫在后面都管用
遞歸函數(shù):
函數(shù)內(nèi)部不斷的調(diào)用本身,以達(dá)到循環(huán)效果
function fun(num){
if(num<=1){
return 1;
}else{
return num * fun( --num );
//如果num大于1那么它將在它內(nèi)部再調(diào)用一次它自身
}
}
console.log( fun(3) );
以上的代碼的執(zhí)行過程相當(dāng)于:
function fun(3){
if(num<=1){
return 1;
}else{
return 3 * function fun(2){
if(num<=1){
return 1;
}else{
return 2 * function fun(1){
if(num<=1){
return 1;
//num小于或等于1,返回1
}else{
return 2 * fun( --num );
}
};
//如果num大于1那么它將在它內(nèi)部再調(diào)用一次它自身
}
};
//如果num大于1那么它將在它內(nèi)部再調(diào)用一次它自身
}
}
console.log( fun(3) );
遞歸函數(shù)在調(diào)用自身的時(shí)候相當(dāng)于是數(shù)個(gè)函數(shù)的嵌套,JS執(zhí)行代碼的過程是逐行執(zhí)行,執(zhí)行遞歸函數(shù)時(shí)同樣,一段代碼相當(dāng)于從最里面開始向外一層一層的執(zhí)行的函數(shù)的集合,所以千萬不要被調(diào)用自身的那一小段代碼迷惑:
function fun(num){
console.log(num);
if(num<1){
console.log("------");
}else{
fun(num-1);//當(dāng)num不小于1的時(shí)候,調(diào)用自身,
};
console.log(num);
};
//相當(dāng)于嵌套多個(gè)函數(shù),每個(gè)函數(shù)內(nèi)都有console.log(num);
fun(2);
//得到的結(jié)果是2 1 0 ------ 0 1 2
閉包函數(shù):
可以將函數(shù)內(nèi)部的變量返回到外部,在外部訪問
作用域
說到閉包,就不得不說一下作用域,JS中的作用于有局部作用域個(gè)全局作用域:
- 在函數(shù)外聲明的變量為全局作用域,任何函數(shù)的任何層嵌套都可以訪問,
- 在函數(shù)內(nèi)聲明的變量為局部作用域,只有在函數(shù)內(nèi)和本函數(shù)嵌套的任意層函數(shù)可以訪問,
- 簡單來說,就是局部可以訪問全局,而全局不能訪問局部
var a = "我是全局變量";
function fun() {
var b = "我是局部變量";
console.log("我可以訪問"+a+"也可以訪問"+b+"?。。?);
}
fun();
console.log("我可以訪問"+a+"但我訪問不了"+b+"?。。?);
//函數(shù)內(nèi)的代碼會(huì)被正常執(zhí)行,但是函數(shù)外的代碼會(huì)報(bào)錯(cuò),找不到變量b
閉包函數(shù)
是通過return給函數(shù)內(nèi)嵌套的函數(shù)內(nèi)定義一個(gè)變量,然后將其返回出來,直到返回到需要的層域或全局:
function fun(){
return function(){
//將這個(gè)函數(shù)返回給上層函數(shù)
var a = 10;
return a;
//將變量a返回給函數(shù)
}
}
console.log(fun()());
閉包在JS中還有很多應(yīng)用,也有很多的坑,這里先簡單了解一下
立即執(zhí)行函數(shù):
立即執(zhí)行函數(shù)也是匿名函數(shù),但它和普通匿名函數(shù)所不同的是:匿名函數(shù)無法調(diào)用,而立即執(zhí)行函數(shù)是調(diào)用自身且只可調(diào)用一次,如下:
//普通匿名函數(shù)
function (){
console.log("我不能執(zhí)行,因?yàn)槲覠o法調(diào)用")
};
//立即執(zhí)行函數(shù)
;(function (){
console.log("我是立即執(zhí)行函數(shù),我調(diào)用我自己")
})();
//因?yàn)闉g覽器的原因,最好在函數(shù)前后各加一個(gè)分號,才不會(huì)因?yàn)槟承┰虺鲥e(cuò)
你發(fā)現(xiàn)這兩者的區(qū)別了沒?沒錯(cuò),其實(shí)就是在函數(shù)后加上了一個(gè)括號,它的形式其實(shí)和其他類型的函數(shù)是一樣的
//定義一個(gè)函數(shù)
function myfun(){
}
//調(diào)用一個(gè)函數(shù)
myfun();
//myfun() == function myfun(){ }
因?yàn)闉g覽器會(huì)自動(dòng)給代碼添加分號,為了讓瀏覽器認(rèn)為匿名函數(shù)是一個(gè)整體,我們就需要用括號將整個(gè)函數(shù)包裹起來,然后在后面添加括號來調(diào)用,原理等同普通函數(shù)通過函數(shù)名調(diào)用,
注意: 在代碼后需要添加分號結(jié)尾,為了防止前面因?yàn)槁┘臃痔枌?dǎo)致報(bào)錯(cuò),所以最好在代碼前也加上一個(gè)分號。