函數是什么?在JavaScrit中函數是一段代碼,只定義一次,但可以被執行或調用任意次。當然不是上圖的含樹。對于函數我想干嘛用的大家都知道,這里沒什么好贅述的。
在深入的學習之前,我先了解了一下。做了一點知識預覽。
- 函數是參數化的:
function(x,y){}
意思就是()里面的可以傳參數,然后拿來用。函數聲明的時候()里面的參數叫形參,調用的時候()里面的參數叫實參(除了實參,調用的時候還會擁有一個本次調用的上下文this)。 - 如果函數作為對象的屬性,就稱他為對象的方法。當調用這個函數的時候他的上下文就是這個對象,即this的值。用于初始化一個對象的函數叫構造函數。
- 函數即對象,所以可以像使用對象那樣使用他,比如:給他添加屬性、把他賦值給變量、作為實參傳遞給其他函數等等。
- 函數中可以嵌套函數,閉包就是嵌套函數的功勞啊。
函數的定義
函數的定義有兩種方法:
- 函數語句
function add(x,y){
return x+y;
}
上面的就是函數語句定義的函數。其實就是創建了一個新的函數對象,并將其賦值給變量add。函數的名字實際是看不見的,add僅僅是變量的名字。函數還可以賦值給其他變量var a = add; a(1,2);
。
- 函數表達式
書中原文
用函數表達式來定義函數指適用于它作為一個大的表達式的一部分,比如在賦值和調用中定義函數
/*這就相當于將函數add賦值給了a,如果我們要使用函數add就不能再直接add(2,4)了,變成了a(2,4)。
為何呢?因為這時的函數add()不再是一個全局的對象了。
*/
var a = function add(x,y){
return x+y;
}
/*這里函數的名稱被省略了。
為何要省略?1、相當劃出一塊私有作用域,避免數據污染。2、執行完就銷毀,避免內存長駐。
什么情況下不省略?需要指代自己的時候 */
var b = function (x,y){
return x-y;//return會使函數停止,所以return后面的語句都不會執行;
console.log('look at me!');
}
var f = function fact(x){
if(X=1)return 1;else return x*fect(x-1);//這里就指代了自己
}
//定義后立即調用,這是一種很新奇的寫法,但是用到的場景太少了。最外層的括號不能去到,去到就會被當作是定義函數啦。這種寫法我還不能給出合理的解釋,以后補充。
var c = (function(x,y){return x*y}(2,4));
上面代碼塊的注釋很重要!!!還需要注意的是,函數聲明語句會被提前,但是用函數表達式聲明函數,變量的聲明提前了,但是變量的賦值卻沒有提前,所以在聲明前調用會報錯。
函數調用
函數在定義的時候是不會執行的,只有調用函數時才會執行。調用函數有4種方法。
- 作為函數調用
function add (x,y) {
return x+y;
}
var a = add; //將函數add賦值給變量a
//以下都是作為函數調用
add(1,2)
a(1,2)
var b =(function(x,y){return x+y;}(1,2))
- 作為方法
一個方法無非就是保存在一個對象的屬性里的函數。比如有一個對象a,a對象有一個屬性b,有一個方法f。那么a.b=f;
就把f函數賦值給了a對象的屬性b。就等于給a對象定義了方法b。
//作為方法調用
a.b();
a.['b']()//這種寫法也是可以的
//如果需要傳參
a.b(12,22);
前面說過,作為方法調用上下文就是它所屬的對象。即this指向對象。
var a ={
b:1,
c:2,
add: function(){return this.b+this.c;}
}
a.add();//這里返回3
需要注意的是this是一個關鍵字,不是變量也不是屬性名。不可給this賦值。
this沒有作用域的限制,嵌套的函數沒有辦法繼承this,這是就需要將this賦值給一個變量。通常都用self作為變量名。
var o ={
a: 1,
add: function(){
var self = this;
function(){
return self .a;//這里如果使用this,那么this不是全局對象就是undefined
}
}
}
- 構造函數調用
如果函數或者方法調用之前帶有關鍵字new,它就構成了構造函數調用。
構造函數是什么?我更傾向于這樣的一種解釋——JavaScript中沒有類,只有new運算來模擬類,構造函數無從談起。在其他語言中,比如java,構造方法用于返回該類的對象。
附上連接http://www.2cto.com/kf/201402/281841.html
這里說的很詳細。
如果構造函數不需要傳參,那么下面兩種寫法等價:
var a = new Object();
var a = new Object;
- 使用call()和apply()間接調用
這種情況在以后call和apply的介紹時一并介紹。
函數的實參和形參
前面說過形參就是函數定義是()中的參數,實參就是調用函數時()中實際傳遞的參數。JavaScrit在定義和調用時都沒有指定參數的類型。調用時甚至不檢查傳入的參數個數。
用例子說明一切
假設有這么一個函數:
function add(x,y){
return x+y;
}
1、當實參比形參少的時候add(1);
,這時候x=1,而y將被設置為undefined。為了避免這樣的情況。應該給省略的參數賦一個合理的默認值。
function add(x,y){
if(y === undefined) y = 0;
//還有一種更簡潔的寫法 y=y || 0;
return x+y;
}
2、當實參比形參多的時候add(1,2,3);
,形參只有兩個只能對應前兩個實參,第三個實參將被忽略。這個時候,就要用到arguments了。
arguments并不是什么高深的東西,就兩句話——arguments指向實參對象的引用,是一個類數組對象(是對象不是數組)。可以像使用數組一樣使用他。
function add(x,y){
var a = 0;
for(var i = 0;i<arguments.length;i++){
a += arguments[i];
}
return a;
}
上面的代碼不管傳多少個參數都可以將所有實參的和返回了,但如果我想要判斷我傳入的實參的類型呢?比如我只想將數字相加,其他都滾蛋。
function add(x,y){
var a = 0;
for(var i = 0;i<arguments.length;i++){
if(argument[i].isNumber()){
a += arguments[i];
}
}
return a;
}
像這種函數,也叫做不定實參函數。這種函數的實參個數不能為零(你要是一個實參都不傳那還用個卵的arguments)。arguments[]對象最適合的應用場景是在這樣一類函數中,這類函數包含固定個數的命名和必須參數,以后隨個數不定的可選參數。
-
callee(被召者)和caller(呼叫者)屬性
這兩個屬性是實參對象arguments的屬性。這兩個屬性在嚴格模式下讀寫都會產生類型錯誤(在webstorm里會飄紅)。書上說:callee指代當前正在執行的函數。聽著有點像call;caller指代調用當前正在執行的函數的函數。沒聽懂。附上一個連接便于理解:http://www.cnblogs.com/dingyuanxin/p/4176310.html
實參
這里還是詳細的說一下實參。當函數形參超過三個時,要記住正確的順序還是挺麻煩的,那在多個形參傳遞的時候,應該怎么避免這種情況呢?——傳一個對象。例如:
function getPersonInfo(name,age,sex) {
console.log(name+age+sex);
}
//要是我這樣傳,name就成了18,age成了王二了。
getPersonInfo('18','王二','男');
//所以可以這樣寫
function getPersonInfo(args){
console.log(args.name+args.age+args.sex);
}
getPersonInfo({name:'王二',age'18',sex:'男'});
實參類型
實參可以是任意類型——對象、函數、字符串、null、undefined等等。有些時候,為了確保函數執行不報錯,會對實參的類型進行判斷,這樣的判斷很重要但經常被忽略。