函數(shù)的定義語法
function 函數(shù)名(){
代碼段
}
function是一個關(guān)鍵字庇麦,函數(shù)的名字自定義放典,但取名的規(guī)則和變量定義的規(guī)則相同。定義好的函數(shù)并不會自己執(zhí)行钝荡,而是需要我們進行調(diào)用。
函數(shù)的調(diào)用
語法:
函數(shù)名()
舉例:
// 定義函數(shù):求兩個數(shù)的和
function fn(){
var a = 1;
var b = 2;
var c = a + b;
console.log(c);
}
// 函數(shù)定義好以后舶衬,不會自動執(zhí)行埠通,需要手動調(diào)用
fn();
賦值式函數(shù)
將一個函數(shù)代碼段賦值給一個變量,這個變量的類型是function類型,變量的值為函數(shù)的代碼逛犹,可以正常的調(diào)用端辱,但如果給他賦值的函數(shù)是有名稱的,那么這個函數(shù)名稱失效(調(diào)用后報錯 函數(shù)名 is not defined)一般可以在賦值時省略這個名稱虽画。
例子:
var a = function zhubi() {
console.log('zhubiluohuihuang');
}
a();
console.log(a);
console.log(typeof a);
zhubi()
控制臺查看結(jié)果如下:
匿名函數(shù)
沒有名字的函數(shù)叫做匿名函數(shù)舞蔽。匿名函數(shù)在空間中不能單獨的存在,必須用小括號將它包裹码撰。
(function(){
console.log(13)
})
匿名函數(shù)用一種特殊的方式調(diào)用渗柿,引出了新的概念,自調(diào)用函數(shù)脖岛。
自調(diào)用函數(shù)
調(diào)用匿名函數(shù)的語法:
(function(){
console.log(13)
})()
這樣的函數(shù)在定義好后被立即調(diào)用朵栖,同時自調(diào)用函數(shù)還有兩種不同調(diào)用的寫法,不給函數(shù)加小括號而是改用~
和柴梆!
!function(){
console.log(14);
}()
~function(){
console.log(14);
}()
PS:自調(diào)用的函數(shù)也可以傳遞參數(shù)陨溅。
(function(a,b){
var c = a + b;
document.write(c);
})(1,2);
帶參數(shù)的函數(shù)
在函數(shù)中的變量需要根據(jù)需要發(fā)生變化時绍在,就可以定義帶參數(shù)的函數(shù)门扇。形參聲明的方法將變量寫在定義函數(shù)的()中,多個變量用,
隔開偿渡。
例如:
function zizeng(a){ // 叫做形參 - 形式上的參數(shù)
var b = a + 1;
console.log(b);
}
調(diào)用時臼寄,需要給形參賦值,在調(diào)用的()
中從左到右一一對應(yīng)的寫入需要付給形參的值溜宽,這些值被稱為實參脯厨。
zizeng(1); // 實參 - 實際上的參數(shù),實參其實就是給形參賦值
如果沒有實參給形參賦值坑质,形參默認(rèn)是undefined合武,如果實參數(shù)量超出了形參临梗,超出的實參是無效的
函數(shù)的本質(zhì)
當(dāng)我們?nèi)フ{(diào)用函數(shù)的時候,通過函數(shù)的名稱就可以調(diào)用到稼跳,那說明我們在定義函數(shù)的時候盟庞,函數(shù)就已經(jīng)保存起來了,所以下面才能調(diào)用出來汤善。
調(diào)用函數(shù)什猖,就相當(dāng)于將這段代碼拿出來執(zhí)行。
函數(shù)的優(yōu)點
- 實現(xiàn)了代碼的可重用性
- 實現(xiàn)了模塊化編程
帶返回值的函數(shù)
如果我們需要一個函數(shù)調(diào)用之后得到的結(jié)果红淡,就需要帶返回值的函數(shù)不狮。返回函數(shù)的結(jié)果,在函數(shù)中使用return
關(guān)鍵字在旱,后面跟上要返回的結(jié)果摇零,可以使一個變量也可以是其他。
例如:
function zhubi(a, b) {
var c;
c = a + b;
return c;//返回c為函數(shù)計算的結(jié)果
}
var d = zhubi(3,5) * 4;//執(zhí)行這里的計算需要return的到函數(shù)計算的結(jié)果
console.log(d);//返回結(jié)果為32
return關(guān)鍵字除了可以給函數(shù)調(diào)用返回結(jié)果桶蝎,還可以結(jié)束函數(shù)運行:
雖然在函數(shù)中有一個字符串輸出的代碼驻仅,但因為return結(jié)束了函數(shù)的進程,所以代碼并沒有被輸出登渣。
return只能返回一個結(jié)果噪服,不能返回多個。
若函數(shù)中沒有加return胜茧,那么函數(shù)的返回值是undefined粘优,而如果加了return而return后面沒有數(shù)值,函數(shù)返回的也是undefined呻顽。
預(yù)解析(重要)
js解析器執(zhí)行代碼過程:
1.預(yù)解析js代碼
預(yù)解析的過程就是在代碼中查找var與function兩個關(guān)鍵字雹顺,找到關(guān)鍵字后將,變量定義與函數(shù)賦值的過程提升到整一個代碼的最前端芬位。所以此時變量的值為undefined因為預(yù)解析只是將變量定義放到了代碼的最前端无拗,而沒有賦值的變量值為undefined带到。函數(shù)為初始值的代碼段昧碉。
2.執(zhí)行代碼
開始按順序一行一行解讀代碼
預(yù)解析題目:
題目:
console.log(a)
var a = 10;
test()
function test(){
console.log("this is test function")
}
題目解析:
var a
function test(){
console.log("this is test function")
}
console.log(a) // 前面有定義過變量a,沒有賦值揽惹,所以變量的值為undefined
a = 10; // 將a的值改變?yōu)?0
test() // 前面有定義過函數(shù)被饿,內(nèi)存中能找到,所以調(diào)用成功
一些題目:
// 第1題
console.log(num)
var num = 100
//輸出undefined
// 第2題
fn();
function fn() {
console.log(123);
}
//輸出123 因為預(yù)解析
// 第3題
console.log(fn)
fn()
var fn = function () {
console.log(123);
}
//第一個輸出undefined
//第二個報錯 fn is not a function
//相當(dāng)于 預(yù)解析先定義了一個變量fn 但fn為undefined但不是一個函數(shù) 不可以用來調(diào)用
// 第4題
fun()
var fn = function () {
console.log('我是 fn 函數(shù)')
}
function fun() {
console.log('我是 fun 函數(shù)')
}
fn()
fn = 100
fn()
//
// 第5題
fn()
function fn() {
console.log('我是一個 fn 函數(shù)')
}
fn()
var fn = 100
fn()
//我是一個fn函數(shù)
//我是一個fn函數(shù)
//報錯
//
/*function fn() {
console.log('我是一個 fn 函數(shù)')
}
var fn
函數(shù)和變量的定義詞同名 保留函數(shù) 忽視變量聲明聲明
fn()
執(zhí)行函數(shù)
*/
// 第6題
var fun = 200
fun()
var fun = function () {
console.log('我是一個 fun 函數(shù)')
}
fun()
// 第7題
var a = b
a = 0
b = 0
console.log(a)
console.log(b)
//沒有定義變量b所以報錯 報錯后 后面的代碼不執(zhí)行
// 第8題
console.log(num)
if (false) {
var num = 100
}
//輸出undefined
預(yù)解析的總結(jié):
1.如果變量名和函數(shù)名重名了搪搏,則保留函數(shù)預(yù)解析狭握,而忽視變量預(yù)解析。
2.不執(zhí)行的代碼中如果有變量或者函數(shù)的定義疯溺,這些函數(shù)與變量也會被預(yù)解析论颅。因為預(yù)解析在執(zhí)行之前哎垦。
3.如果js的代碼報錯,則之后的代碼不再執(zhí)行恃疯。
4.賦值式函數(shù)在預(yù)解析時算作定義變量漏设,而不算定義函數(shù)。
5.函數(shù)會在函數(shù)內(nèi)部進行預(yù)解析
函數(shù)的調(diào)試
作用域
作用域的定義:能起到作用的區(qū)域今妄,因為定義在不同區(qū)域的變量郑口,其作用域是不同的。
作用域的分類
全局作用域:
不在任何一個函數(shù)中定義的變量叫做全局變量盾鳞,全局變量的作用域是整一個script區(qū)域犬性。
<script>
var a = 6; //全局變量a,作用域范圍也包括了下一個script
console.log(a);
function fn(){
console.log(a);
}
fn()
</script>
局部作用域:
在函數(shù)內(nèi)部定義的變量腾仅,他的作用域的范圍就是這個函數(shù)乒裆,在函數(shù)外不能使用這個變量。
<script>
function fn(){
var a = 6;
console.log(a);
}
fn() //輸出6
console.log(a);//報錯a is not defined
</script>
如果全局和局部有相同的變量名攒砖,局部輸出時先考慮當(dāng)前的作用域缸兔。
<script>
var a = 12;
function fn(){
var a = 6;
console.log(a);
}
fn() //輸出6
console.log(a);//輸出12
</script>
作用域鏈
函數(shù)是寫在全局當(dāng)中的,所以局部作用域是嵌套在全局作用域之中的吹艇,如果再在函數(shù)中嵌套另一個函數(shù)惰蜜,則在局部作用域中又會再嵌套一個局部作用域。這樣就形成了作用域的嵌套受神,由作用域的嵌套引發(fā)的鏈?zhǔn)浇Y(jié)構(gòu)就叫做---作用域鏈抛猖。
var a = 10;
function fn() {
var b = 20;
fun()
function fun() {
var c = 30;
console.log(b);//輸出20
}
console.log(a);//輸出10
}
fn()
作用域鏈的規(guī)則
- 當(dāng)使用一個變量時,包括:用變量作為值進行賦值鼻听、用變量進行計算箭昵、輸出變量,會先在當(dāng)前作用域中尋找變量是否有定義過變量境蜕,若定義過叹话,正常使用;若沒有在這個定義域中定義醉拓,則按照作用域鏈伟姐,返回這個定義域的上一級尋找,同理若找到正常使用亿卤,若沒有找到則重復(fù)這個操作直到全局作用域中愤兵。若在全局定義域中有,則還是可以正常使用排吴,若沒有則在使用中報錯:變量名 is not defined.
- 當(dāng)給一個變量進行賦值時秆乳,現(xiàn)在當(dāng)前作用域中查找是否定義過這一個變量,如果定義過則給這個變量賦值。若沒有屹堰,則和使用變量的原理一樣肛冶,直到全局作用域。不同的是若是全局作用域也沒有定義過這個變量扯键,則規(guī)則是在全局定義這個變量淑趾,并對他賦值,值為你書寫的值忧陪。
一些練習(xí):
// function fn() {
// console.log(num) // undefined扣泊,原因為在函數(shù)中也會進行預(yù)解析
// return
// var num = 100
// }
// fn()
// console.log(num); // 報錯
// var num = 10;
// fn1();
// function fn1() {
// console.log(num);
// var num = 20;
// }
/*
預(yù)解析:
var num
function fn1() {
var num
console.log(num); // undefined
num = 20;
}
num = 10;
fn1();
函數(shù)中也會預(yù)解析
*/
// var a = 18;
// fn2();
// function fn2() {
// var b = 9;
// console.log(a); // 18
// console.log(b); // 9
// }
// fn3();
// console.log(c);
// console.log(b);
// console.log(a);
// function fn3() {
// var a = b = c = 9; // b=9
// console.log(a); // 9
// console.log(b); // 9
// console.log(c); // 9
// }
// var a = b = c = 9
/*
相當(dāng)于:
var a = 9
b = 9
c = 9
*/
// console.log(a,b,c);
// function fn(a) {
// // var a = 10
// // -------------------
// function a() {
// console.log('我是函數(shù) a')
// }
// console.log('我是 fn 函數(shù)')
// a()
// }
// fn(10)
習(xí)題總結(jié):
- 全局中有預(yù)解析,局部中也有預(yù)解析嘶摊,局部的預(yù)解析只在局部中進行對全局沒有影響延蟹。
- 在使用
var a = b = c =9
這種方式給變量賦值時,只有第一個變量是有定義的過程也就說只有第一個變量定義等同于var a=9
其余都是直接給變量賦值叶堆。 - 函數(shù)定義好以后阱飘,函數(shù)名就跟變量名一樣,可以使用函數(shù)名修改這個空間中的值虱颗。
- 局部的預(yù)解析會在形參賦值之后沥匈,預(yù)解析中的函數(shù)會覆蓋掉形參賦的值。
遞歸函數(shù)
遞歸函數(shù)就是在函數(shù)內(nèi)部調(diào)用自己忘渔,形成循環(huán)高帖。
使用遞歸函數(shù)時,應(yīng)該注意要有一個停止函數(shù)的條件畦粮,否則會形成死循環(huán)散址。
寫遞歸函數(shù)的步驟:
- 先書寫一個空函數(shù),并調(diào)用
- 調(diào)用時傳入實參宣赔,定義形參
- 書寫大致的代碼
- 根據(jù)想要實現(xiàn)的效果給遞歸函數(shù)一個停止的條件
用遞歸函數(shù)實現(xiàn)效果:
// 求10的階和预麸,即:10+9+8+...+1
function facSum(num){
if(num == 1){
return 1;
}
return num + facSum(num-1);
}
var res = facSum(10);
console.log(res); // 55