前言
由于javascript中的函數(shù)定義方式比較特殊拄丰,并且每種的特點(diǎn)都不相同转唉,所以本文介紹一下JS幾種函數(shù)定義方式和各自的優(yōu)缺點(diǎn)劝堪,并且對(duì)JS中的函數(shù)聲明和變量聲明的hoist(提前)進(jìn)行簡(jiǎn)略說(shuō)明贬墩。
函數(shù)定義方式
函數(shù)生命式
這是js中比較常用的方式:
function funcname(a,b){
//處理代碼
}
當(dāng)該函數(shù)被調(diào)用呀闻,函數(shù)體才會(huì)被得到執(zhí)行精肃。
function構(gòu)造函數(shù)方式
var funcname = new Function ('x', 'y', 'alert(x+y)');
該定義方式中前面兩個(gè)是參數(shù)秤涩,參數(shù)可以是任意多個(gè)字符串;第三個(gè)是函數(shù)體司抱,可以包含任何 JavaScript 語(yǔ)句筐眷,語(yǔ)句之間用分號(hào)隔開(kāi)。如果沒(méi)有參數(shù)习柠,傳一個(gè)函數(shù)體即可匀谣。由于傳遞給 Function () 函數(shù)中,沒(méi)有一個(gè)字符串是用來(lái)聲明函數(shù)名的资溃,所以它是一個(gè)匿名函數(shù)武翎。
函數(shù)直接量
var funcname = function(a,b){
//處理代碼
}
區(qū)別
三種方式的區(qū)別,可以從作用域溶锭、效率以及加載順序來(lái)區(qū)分宝恶。首先,從作用域上來(lái)說(shuō)趴捅,函數(shù)聲明式和函數(shù)直接量使用的是局部變量垫毙,而 Function()構(gòu)造函數(shù)卻是全局變量:
var y = 'global';
function a(){
var y = 'local a';
return y;
}
alert(a());//顯示'local a'
var b = function(){
var y = 'local b';
return y;
}
alert(b()) //顯示'local b'
function c(){
var y = 'local c';
return new Function('return y');
}
alert(c()());//顯示'global',因?yàn)镕unction()返回的是全局變量y拱绑,而不是函數(shù)體內(nèi)的局部變量综芥。
再看效率,F(xiàn)unction()構(gòu)造函數(shù)的效率要低于其他兩種方式猎拨,尤其是在循環(huán)體中毫痕,因?yàn)闃?gòu)造函數(shù)每執(zhí)行一次都要重新編譯,并且生成新的函數(shù)對(duì)象迟几。
最后是加載順序消请,function 方式(即函數(shù)聲明式)是在 JavaScript 編譯的時(shí)候就加載到作用域中,而其他兩種方式則是在代碼執(zhí)行的時(shí)候加載,如果在定義之前調(diào)用它类腮,則會(huì)返回 undefined
臊泰。
函數(shù)聲明和變量聲明的提前
從上面對(duì)三種定義方式的比較來(lái)看,只有函數(shù)聲明方式定義的函數(shù)會(huì)在JS編譯的時(shí)候就加載到作用域中蚜枢,由此想到了JS中對(duì)聲明的提前
變量聲明提前:
(function() {
console.log(a);//undefined
var a = "Now it's defined!";
console.log(a);//"Now it's defined!"
})();
在未對(duì)a進(jìn)行定義和聲明的時(shí)候輸出a卻提示undefined但沒(méi)有報(bào)錯(cuò)缸逃,這其實(shí)是 JavaScript 解析器搞的鬼针饥,解析器將當(dāng)前作用域內(nèi)聲明的所有變量和函數(shù)都會(huì)放到作用域的開(kāi)始處,但是需频,只有變量的聲明被提前到作用域的開(kāi)始處了丁眼,而賦值操作被保留在原處。
由于 JavaScript 具有這樣的“怪癖”昭殉,所以你會(huì)看到很多編碼指南建議大家將變量聲明放在作用域的最上方苞七,這樣就能時(shí)刻提醒自己注意了。
函數(shù)聲明提前:
第一種情況:
isItHoisted();//"Yes!"
function isItHoisted() {
console.log("Yes!");
}
如上所示挪丢,JavaScript 解釋器允許你在函數(shù)聲明之前使用蹂风,也就是說(shuō),函數(shù)聲明并不僅僅是函數(shù)名“被提前”了乾蓬,整個(gè)函數(shù)的定義也“被提前”了惠啄!所以上述代碼能夠正確執(zhí)行。
第二種情況:
funca();//"Definition hoisted!"
funcb();//拋出錯(cuò)誤:undefined is not a function
function funca() {
console.log("Definition hoisted!");
}
var funcb = function () {
console.log("Definition not hoisted!");
};
我們做了一個(gè)對(duì)比任内,funca函數(shù)被妥妥的執(zhí)行了撵渡,符合第一種類型;funcb函數(shù) 變量“被提前”了死嗦,但是他的賦值(也就是函數(shù))并沒(méi)有被提前趋距,從這一點(diǎn)上來(lái)說(shuō),和前面我們所講的變量“被提前”是完全一致的越走,并且棚品,由于“被提前”的變量的默認(rèn)值是 undefined
靠欢,所以報(bào)的錯(cuò)誤屬于“類型不匹配”廊敌,因?yàn)?undefined 不是函數(shù),當(dāng)然不能被調(diào)用门怪。
總結(jié)
通過(guò)上面的講解可以總結(jié)如下:
- 變量的聲明被提前到作用域頂部骡澈,賦值保留在原地
- 函數(shù)聲明方式定義函數(shù)整個(gè)“被提前”
- 函數(shù)直接量定義函數(shù)的方式只有變量“被提前”了,函數(shù)沒(méi)有“被提前”
正好和前面所說(shuō)的函數(shù)定義的三種不同方式的區(qū)別是吻合的掷空。
所以提醒大家:作為最佳實(shí)踐肋殴,變量聲明一定要放在作用域/函數(shù)的最上方(JavaScript 只有函數(shù)作用域!)坦弟。