一.初步了解函數(shù)
為什么要有函數(shù)呢,我們先從函數(shù)的功能出發(fā),來(lái)認(rèn)識(shí)一下函數(shù)給我們帶來(lái)的好處
比如:
if(1>0){
console.log("a");
console.log("b");
console.log("c");
}
if(2>0){
console.log("a");
console.log("b");
console.log("c");
}
if(3>0){
console.log("a");
console.log("b");
console.log("c");
}
當(dāng)我們滿足一定條件要執(zhí)行一塊代碼,要執(zhí)行一個(gè)功能,這就這個(gè)功能在很多條件滿足的情況下都要執(zhí)行.,我要是這么寫,你會(huì)發(fā)現(xiàn)我代碼的重復(fù)性非常高,你看這些代碼都是重復(fù)的.
還記得我們css中的屬性重復(fù)吧,我們可以通過分組解決重復(fù)代碼的復(fù)用問題
那這種重復(fù)在我們js中也是不被推薦的 ,我們管這種重復(fù)交耦合,耦合就是重復(fù)冗余的.
那如何解決這種耦合問題呢?
js中也存在一個(gè)這種封裝的盒子, 來(lái)對(duì)代碼進(jìn)行復(fù)用, 這個(gè)封裝的合資就是函數(shù)
簡(jiǎn)而言之, 就是講一些復(fù)用的代碼封裝在一個(gè)函數(shù)中,通過函數(shù)在不同地方的使用來(lái)復(fù)用封裝的代碼 直接上代碼康康:
functiontest(){
console.log("a");
console.log("b");
console.log("c");
}
if(1>0){
test();
}
if(2>0){
test();
}
if(3>0){
test();
}
函數(shù)組基本的作用就是簡(jiǎn)化代碼, 提高開發(fā)效率
1. 函數(shù)的定義和調(diào)用
函數(shù)和變量一樣, 先定義(聲明)在使用
//定義一個(gè)函數(shù),函數(shù)就是一組語(yǔ)句的集合
functionhaha(){
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
//調(diào)用函數(shù)
haha();
1.1 函數(shù)的聲明
定義一個(gè)函數(shù),用關(guān)鍵字function來(lái)定義嫩海,
function就是英語(yǔ)“功能”的意思绘闷。表示這里面定義的一個(gè)功能搅窿。
function后面有一個(gè)空格罚屋,后面就是函數(shù)名字鸡捐,函數(shù)的名字也是標(biāo)識(shí)符璧瞬,因此命名規(guī)范和變量命名是一樣的户辫。
名字后面有一對(duì)兒圓括號(hào),里面放置參數(shù)嗤锉,一會(huì)兒在介紹參數(shù)渔欢。然后就是大括號(hào),大括號(hào)里就是封裝的函數(shù)語(yǔ)句
定義一個(gè)函數(shù):
function? 函數(shù)名(){
}
函數(shù)如果不調(diào)用瘟忱,那么里面的語(yǔ)句就一輩子不會(huì)執(zhí)行奥额,不調(diào)用就等于白寫。
1.2 函數(shù)的調(diào)用
調(diào)用一個(gè)函數(shù)的方法非常簡(jiǎn)單访诱,函數(shù)名后面加一個(gè)()? 垫挨,() 是一個(gè)運(yùn)算符,表示執(zhí)行一個(gè)函數(shù)触菜。
functiontheFirstName(){
}
console.log(theFirstName);// 弱類型的語(yǔ)言用于不輸出內(nèi)存地址,,輸出指針指向的房間
執(zhí)行函數(shù)語(yǔ)句:
函數(shù)名()
一旦調(diào)用了函數(shù)九榔,函數(shù)內(nèi)部的語(yǔ)句就會(huì)執(zhí)行。
函數(shù)是一些語(yǔ)句的集合,仔細(xì)體會(huì), 感覺上函數(shù)就是讓一些零散語(yǔ)句成為一個(gè)軍團(tuán)哲泊,集體作戰(zhàn)剩蟀。要不出動(dòng)都不出動(dòng),要出動(dòng)就全出動(dòng)攻旦。前提得到命令(調(diào)用)才會(huì)出動(dòng)(執(zhí)行)喻旷。
2. 函數(shù)的參數(shù)
2.1 參數(shù)的了解
上一節(jié)了解了函數(shù)的定義和使用, 但是會(huì)發(fā)現(xiàn), 函數(shù)內(nèi)部的語(yǔ)句都是相同, 那么如果我們需要在不同的地方, 使用不同語(yǔ)句, 就回有問題, 那么如何解決這個(gè)問題呢? 實(shí)際上我們可以通過“參數(shù)”這個(gè)東西,來(lái)讓語(yǔ)句有差別牢屋。
定義函數(shù)的時(shí)候且预,內(nèi)部語(yǔ)句可能有一些懸而未決的量,就是變量烙无,這些變量锋谐,我們要求在定義的時(shí)候都羅列在小括號(hào)中:
比如:
functionfun(a){
// 參數(shù)就相當(dāng)于隱式的在函數(shù)體呢var a;
console.log("我第"+a+"次說(shuō)愛你");
}
調(diào)用的時(shí)候,要把這個(gè)變量的真實(shí)的值截酷,一起寫在括號(hào)里涮拗,這樣隨著函數(shù)的調(diào)用,這個(gè)值也傳給了a:
執(zhí)行這個(gè)函數(shù):
fun(88);
羅列在function小括號(hào)中的參數(shù)迂苛,叫做形式參數(shù)三热;調(diào)用時(shí)傳遞的數(shù)值,叫做實(shí)際參數(shù)三幻。
形式參數(shù)就像占位置,先把位置站好,等你來(lái)賦值;
2.2? 參數(shù)個(gè)數(shù)
參數(shù)可以有無(wú)數(shù)個(gè)就漾,用逗號(hào)隔開。
//有多少形式參數(shù)都可以念搬,都羅列出來(lái)
functionfun(a,b){
console.log(a+b);
}
fun(3,5);//輸出8
fun(8,11);//輸出19
定義函數(shù)的時(shí)候抑堡,參數(shù)是什么類型的沒寫,不需要指定類型:
functionsum(a,b){
console.log(a+b);
}
也就是說(shuō)調(diào)用的時(shí)候朗徊,傳進(jìn)去什么什么類型首妖,就是a、b什么類型
sum("5",12);
輸出512爷恳,做的是連字符的運(yùn)算有缆。
有了參數(shù)以后,函數(shù)就實(shí)現(xiàn)了真正的功能
functionsum(a,b){
varc=a+b;
console.log(c);
}
sum(1,3);// 這就是一個(gè)求和的功能吧
// 咱們就相當(dāng)于把兩個(gè)函數(shù)的規(guī)則抽象出來(lái)了
// 這個(gè)時(shí)候是不是就像我們數(shù)學(xué)中x,y, 隨便代換值
// 這里面變量就是參數(shù)
// 有了參數(shù)以后這里就變成了抽象規(guī)則了,而不是原來(lái)的那種聚合代碼的作用
// 原來(lái)只是為了聚合代碼 不讓代碼重復(fù) 現(xiàn)在就是抽象規(guī)格的作用了
// 有參數(shù)才變得強(qiáng)大,沒參數(shù)發(fā)現(xiàn)沒什么用
例子:
functionsum(a,b){
if(a>10){
console.log(a-b);
}elseif(a<10){
console.log(a+b);
}else{
consol.log(10);
?? }
}
2.3 實(shí)參和形參個(gè)數(shù)不等(天生不定參數(shù))
我們還可以發(fā)現(xiàn),定義的時(shí)候和調(diào)用的時(shí)候參數(shù)個(gè)數(shù)可以不一樣多温亲,不報(bào)錯(cuò)棚壁。
sum(10);//NaN 因?yàn)橛?jì)算的就是10+undefined = NaN
因?yàn)槲覀冎粋髁艘粋€(gè)參數(shù),b就沒有傳遞铸豁,b被隱式的var了,所以值undefined灌曙。10+undefined就是NaN
sum(10,20,32,23,22,2,4);// 30
只有前兩個(gè)參數(shù)被形參接收了菊碟,后面的參數(shù)無(wú)視了节芥。
2.4. arguments
JS有一個(gè)非常強(qiáng)大的東西,就是每一個(gè)函數(shù)內(nèi)部,都可以使用一個(gè)arguments這個(gè)類數(shù)組對(duì)象头镊。
這個(gè)arguments對(duì)象蚣驼,就涵蓋了所有實(shí)參。
調(diào)用函數(shù)的時(shí)候相艇,比如:
fun(45,436,457,34,23,12);
此時(shí)函數(shù)內(nèi)部颖杏,arguments就有一個(gè)下標(biāo),就依次等于上面調(diào)用的數(shù):
arguments[0]// 45
arguments[1]// 436
……
arguments[5]//12
如果函數(shù)里面有形式參數(shù)列表坛芽,那么是和arguments同步的:
functionfun(a,b){
arguments[0]=8;//改變了第一個(gè)參數(shù)的值
alert(a);//8? 留储,彈出改變后的值
// agruments[0]和a 不是同一個(gè)變量,只是函數(shù)內(nèi)部有一個(gè)繩子,叫映射規(guī)則,就是我變你也的變,你變我也得變
//? 實(shí)參有幾個(gè),arguments就有幾個(gè),此時(shí)
// 沒有arguments[1]
b=2;
console.log(argumnts[1]);
// 就算此時(shí)你把b改為2? 也不會(huì)往arguments里面加了,
// arguments 出生有幾個(gè)就是幾個(gè)
}
fun(45);
arguments的功能,是模擬函數(shù)的重載咙轩,使得同一個(gè)函數(shù)获讳,根據(jù)參數(shù)個(gè)數(shù)的不同,有不同的作用活喊。
那什么是重載呢?
在Java中丐膝,同一個(gè)函數(shù)名,但是參數(shù)個(gè)數(shù)不一樣钾菊,視為是兩個(gè)函數(shù)帅矗。
也就是說(shuō),Java中能夠定義兩個(gè):
functionsum(a,b){
}
functionsum(a,b,c){
}
同名的兩個(gè)function煞烫,都是sum函數(shù)浑此,但是java中是允許這么做的,因?yàn)閰?shù)個(gè)數(shù)不一樣红竭,這種現(xiàn)象叫做重載尤勋。
JavaScript沒有重載的概念:
比如現(xiàn)在我們想設(shè)計(jì)一個(gè)sum函數(shù),如果傳進(jìn)來(lái)一個(gè)參數(shù)茵宪,就得到這個(gè)數(shù)字的加1最冰;如果是2個(gè)參數(shù),那么返回兩個(gè)數(shù)字的和稀火。比如
sum(10);//1
sum(3,4);//7
就要通過arguments.length 實(shí)際參數(shù)的個(gè)數(shù)暖哨,來(lái)進(jìn)行判斷:
functionsum(a,b){
//如果實(shí)際參數(shù)的長(zhǎng)度是1,說(shuō)白了凰狞,你只傳進(jìn)來(lái)一個(gè)參數(shù)
switch(arguments.length){
case1:
return++a;
break;
case2:
returna+b;
break;
?? }
}
更牛逼的篇裁,我們可以無(wú)限參數(shù),設(shè)計(jì)一個(gè)函數(shù)sum赡若,能夠接受無(wú)限參數(shù)
sum(3,4,6,5,8,2)//28
獲取實(shí)參形參個(gè)數(shù),判斷實(shí)參和形參的個(gè)數(shù);
functionsum(a,b,c,d){
if()
}
用函數(shù)算累和
functionsum(){
varresult=0;
for(vari=0;i<arguments.length;i++){
result+=arguments[i]
?? }
console.log(result);
}
sum(1,2,3,4)
這是不是就是不定參的好出吧,系統(tǒng)內(nèi)置的一些函數(shù)都是不定參的.
3. 返回值
3.1? 函數(shù)的返回值
函數(shù)可以通過參數(shù)來(lái)接收東西达布,更可以通過return的語(yǔ)句來(lái)返回值,“吐出”東西逾冬。
既返回也終止
functionsum(a,b){
returna+b;//現(xiàn)在這個(gè)函數(shù)的返回值就是a+b的和
}
console.log(sum(3,8));//sum沒有輸出功能黍聂,就要用console.log輸出
//sum(3,8)實(shí)際上就成為了一個(gè)表達(dá)式躺苦,需要計(jì)算
//計(jì)算后就是11,console.log(11);
函數(shù)只能有唯一的return产还,有if語(yǔ)句除外
程序遇見了return匹厘,將立即返回結(jié)果,返回調(diào)用它的地方脐区,而不執(zhí)行函數(shù)內(nèi)的剩余的語(yǔ)句愈诚。
functionfun(){
console.log(1);
console.log(2);
return;//返回一個(gè)空值
console.log(3);//這行語(yǔ)句不執(zhí)行,因?yàn)楹瘮?shù)已經(jīng)return了
}
fun();//1, 2
console.log(fun());//undefined
functionmyNumber(target){
return+target;
}
varnum=myNumber("123");
console.log(typeofnum+" "+num);
3.2 函數(shù)作為參數(shù)使用
函數(shù)有一個(gè)return的值牛隅,那么現(xiàn)在這個(gè)函數(shù)炕柔,實(shí)際上就是一個(gè)表達(dá)式,換句話說(shuō)這個(gè)函數(shù)就是一個(gè)值媒佣。
所以這個(gè)函數(shù)汗唱,可以當(dāng)做其他函數(shù)的參數(shù)。
sum(3,sum(4,5));//12
//輸出12丈攒,實(shí)際上有兩次執(zhí)行了sum函數(shù)
//先執(zhí)行最內(nèi)層哩罪,計(jì)算出9,然后 sum(3,9)
//就是12
程序從最內(nèi)層做到最外層巡验, sum(3,9)? 12
函數(shù)可以接受很多值际插,返回一個(gè)值,
函數(shù)的意義:
在出現(xiàn)大量程序相同的時(shí)候显设,可以封裝為一個(gè)function框弛,這樣只用調(diào)用一次,就能執(zhí)行很多語(yǔ)句捕捂。
我們?cè)谡{(diào)用一個(gè)函數(shù)的時(shí)候瑟枫,不用關(guān)心函數(shù)內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),甚至這個(gè)函數(shù)是你上網(wǎng)抄的指攒,可以運(yùn)用慷妙。所以這個(gè)東西,給我們團(tuán)隊(duì)開發(fā)帶來(lái)了好處允悦。
模塊化編程膝擂,讓復(fù)雜的邏輯變得簡(jiǎn)單。
4. 應(yīng)用函數(shù)簡(jiǎn)化編程
我們現(xiàn)在做一個(gè)程序隙弛,輸出2~100的所有質(zhì)數(shù)架馋。所謂的質(zhì)數(shù),就是只有1全闷、自己兩個(gè)約數(shù)叉寂,沒有其他約數(shù)。
要把一個(gè)復(fù)雜的問題总珠,拆分為一個(gè)個(gè)小問題屏鳍。
高層的業(yè)務(wù)伊约,就能使用底層的函數(shù)提供的API:
約數(shù)個(gè)數(shù)函數(shù) → 判斷質(zhì)數(shù)函數(shù) → 高層業(yè)務(wù)
//約數(shù)個(gè)數(shù)函數(shù):能夠傳入一個(gè)數(shù)字,吐出來(lái)它約數(shù)的個(gè)數(shù)
functionyueshugeshu(a){
//計(jì)算a這個(gè)數(shù)字約數(shù)的個(gè)數(shù)
varcount=0;
for(vari=1;i<=a;i++){
if(a%i==0){
count++;
? ? ?? }
?? }
returncount;//返回這個(gè)數(shù)字的約數(shù)的個(gè)數(shù)
}
//判斷是否是質(zhì)數(shù)孕蝉,如果一個(gè)函數(shù)的名字取is
//就暗示了將返回布爾值,要么是true要么是false腌逢。是通常做法降淮,不是規(guī)定
//接收一個(gè)參數(shù)m,返回是否是質(zhì)數(shù)t或者f
functionisZhishu(m){
if(yueshugeshu(m)==2){
returntrue;
}else{
returnfalse;
?? }
}
//尋找1~100的質(zhì)數(shù)
for(vari=1;i<=100;i++){
if(isZhishu(i)){
console.log(i);
?? }
}
5. 遞歸
怎么解出來(lái)不重要,這種解題方法一定要知道,這種方式叫遞歸
就是頻繁調(diào)用自己,只有一個(gè)好處簡(jiǎn)化代碼,除此之外,沒有其他好處,
遞歸是不是實(shí)現(xiàn)的塊啊,遞歸是最慢的,特別復(fù)雜的程序不能用遞歸,為什么慢,想想為什么慢
// !n? 階乘
functionmul(){
if(n==1||n==0){
return1;
?? }
returnn*mul(n-1);
}
遞歸的規(guī)律,先執(zhí)行的最后執(zhí)行完
二. 函數(shù)表達(dá)式
定義函數(shù)除了使用function之外搏讶,還有一種方法佳鳖,就是函數(shù)表達(dá)式。就是將函數(shù)聲明賦值給一個(gè)變量
如果現(xiàn)在這個(gè)函數(shù)表達(dá)式中的function不是匿名的媒惕,而是有名字的:
// 命名函數(shù)表達(dá)式
varhaha=functionxixi(a,b){
returna+b;
}
// 這種定義方式
console.log(haha.name);// xixi
那么JS表現(xiàn)非常的奇怪系吩,在外部只能用haha()來(lái)調(diào)用,xixi()非引發(fā)錯(cuò)誤妒蔚!此時(shí)可以console.log()函數(shù)名
console.log(haha);// 為函數(shù)體
console.log(xixi);// Uncaught ReferenceError: xixi is not defined
所以此時(shí)函數(shù)可以沒有名字穿挨,稱為“匿名函數(shù)”,為了今后能夠調(diào)用它肴盏,我們把這個(gè)匿名函數(shù)科盛,直接賦值給一個(gè)變量。
// 匿名函數(shù)表達(dá)式 ?
varhaha=function(a,b){
returna+b;
}
// 因?yàn)楸容^常用,所有以后我們講的函數(shù)表達(dá)式就是指匿名函數(shù)表達(dá)式,
// 如果要講命名函數(shù)表達(dá)式會(huì)特殊強(qiáng)調(diào)
以后想調(diào)用這個(gè)函數(shù)的時(shí)候菜皂,就可以直接使用haha變量來(lái)調(diào)用贞绵。
console.log(haha(1,3));
也就是說(shuō),JS這個(gè)奇怪的特性恍飘,給我們提了個(gè)醒榨崩,定義函數(shù),只能用這兩種方法章母,但是不能雜糅:
第一種,通過函數(shù)聲明定義函數(shù)
functionhaha(){
}
第二種,通過匿名函數(shù)的賦值定義函數(shù)
varhaha=function(){
}
盡量不要用:
varxixi=functionhaha(){
}
三. 函數(shù)聲明的提升(預(yù)解析)
JS在執(zhí)行前母蛛,會(huì)有一個(gè)預(yù)解析的過程,把所有的函數(shù)聲明乳怎,都提升到了最最開頭溯祸,然后再執(zhí)行第一行語(yǔ)句。
所以舞肆,function定義在哪里焦辅,都不重要,程序總能找到這個(gè)函數(shù)椿胯。
//先調(diào)用
fun();//可以彈出警告框筷登,因?yàn)楹瘮?shù)有函數(shù)聲明頭提升的特性
//然后定義
functionfun(){
alert("我是函數(shù),我執(zhí)行了哩盲!");
}
不會(huì)引發(fā)錯(cuò)誤前方,alert能夠彈出狈醉。
函數(shù)聲明會(huì)被提升,但是函數(shù)表達(dá)式卻不會(huì)被提升
函數(shù)表達(dá)式提升的是變量,變量提升后并不是一個(gè)函數(shù),所以在表達(dá)式之前執(zhí)行,會(huì)報(bào)錯(cuò),為類型錯(cuò)誤,因?yàn)椴皇呛瘮?shù).
// ************************************
//函數(shù)表達(dá)式不會(huì)有提升的
fun();//報(bào)錯(cuò)
varfun=function(){
alert("我是函數(shù)惠险,我執(zhí)行了苗傅!");
}
又給我們提了個(gè)醒,沒有極特殊的理由班巩,都要使用function 關(guān)鍵字來(lái)定義函數(shù)渣慕,而不要使用函數(shù)表達(dá)式來(lái)定義函數(shù).
1.函數(shù)優(yōu)先
aaa();//現(xiàn)在這個(gè)aaa到底是函數(shù),還是變量5呢抱慌?
//函數(shù)優(yōu)先逊桦,遇見同名標(biāo)識(shí)符,預(yù)解析階段一定把這個(gè)標(biāo)識(shí)符給函數(shù)
varaaa=5;//定義一個(gè)變量抑进,是5
functionaaa(){
alert("我是aaa函數(shù)强经,我執(zhí)行了");
}
面試很容易考:
foo();
varfoo;
functionfoo(){
console.log(1);
}
foo=function(){
console.log(2);
}
函數(shù)優(yōu)先,現(xiàn)在foo這個(gè)標(biāo)識(shí)符沖突了寺渗,一個(gè)函數(shù)叫做foo匿情,一個(gè)變量也叫作foo。預(yù)解析階段信殊,如果遇見標(biāo)識(shí)符沖突码秉,這個(gè)標(biāo)識(shí)符給函數(shù)。
函數(shù)聲明的提升鸡号,是無(wú)節(jié)操的转砖,強(qiáng)制提升,即使用if語(yǔ)句鲸伴,也會(huì)提升府蔗。
四.IIFE
IIFE就是immediately-invoked function expression,即時(shí)調(diào)用函數(shù)表達(dá)式
如果一個(gè)函數(shù)汞窗,在定義的時(shí)候姓赤,我們就想直接調(diào)用它,就是一個(gè)IIFE仲吏。
我們?cè)噲D在定義函數(shù)的后面不铆,直接寫圓括號(hào):
functionfun(){
alert("哈哈")
}();
控制臺(tái)報(bào)錯(cuò),這是因?yàn)楹瘮?shù)是一個(gè)函數(shù)體裹唆,并不是表達(dá)式誓斥,只有表達(dá)式能夠用()來(lái)執(zhí)行。
所以就要把function fun(){}“降級(jí)”许帐, 從函數(shù)體降級(jí)為表達(dá)式劳坑。方法有很多:
+functionfun(){
alert("哈哈")
}();
-functionfun(){
alert("哈哈")
}();
更通常更常用的:
(functionfun(){
alert("哈哈")
})();
用這種方法定義的函數(shù),名字是無(wú)效的成畦,其他的地方想調(diào)用這個(gè)函數(shù)
fun();
所以IIFE里面的函數(shù)距芬,都是匿名函數(shù):
(function(){
alert("哈哈");
})();
上面就是一個(gè)標(biāo)準(zhǔn)的IIFE涝开。
例子:設(shè)計(jì)一個(gè)函數(shù),這個(gè)函數(shù)接收三個(gè)參數(shù)框仔,比如sum(4,7,9);返回的是前兩個(gè)數(shù)字大的那個(gè)數(shù)字舀武,與第三個(gè)數(shù)字的和。
sum(4,2,3);//7
sum(2,4,3);//7
sum(5,4,3);//8
sum(a,b,c){
return(function(a,b){
returna>=b?a:b;
})(a,b)+c;
}
紅色部分是一個(gè)IIFE离斩,本質(zhì)上是一個(gè)表達(dá)式银舱,表達(dá)式計(jì)算之后,就是值捐腿,什么值呢?a柿顶、b中大的那個(gè)數(shù)字茄袖。
五. 引用類型
我們之前說(shuō)的,基本類型:number嘁锯、string宪祥、boolean、undefined家乘、null
functionfun(){
}
console.log(typeoffun);// funtion