一省撑、函數(shù)的定義
定義函數(shù)有三種方式:函數(shù)聲明充石、函數(shù)表達(dá)式颈畸、Function構(gòu)造函數(shù)(不推薦)
函數(shù)聲明比如:
function sum(num1,num2){
return num1 + num2;
}
函數(shù)表達(dá)式咐旧,其實(shí)就是變量聲明的一種蝶涩,這種定義方式得到的函數(shù)也叫匿名函數(shù)(拉姆達(dá)函數(shù))理朋,因?yàn)閒unction關(guān)鍵字后面沒有函數(shù)名字,只是把這個函數(shù)體賦值給一個變量绿聘。這種方式定義函數(shù)也沒有必要使用函數(shù)名---通過變量名就可以引用函數(shù)嗽上。另外還要注意,此時函數(shù)末尾有一個分號斜友,就像聲明其他變量一樣需要一個分號作為結(jié)尾炸裆。比如:
var sum = function (num1,num2){
return num1 + num2;
};
Function構(gòu)造函數(shù)可以接收任意數(shù)量的參數(shù),但最后一個參數(shù)始終都被看成函數(shù)體鲜屏,而前面的參數(shù)枚舉出了新函數(shù)的參數(shù)烹看。比如:
var sum = new Function("num1","num2","return num1 + num2");? ? ? ? //不推薦
從技術(shù)上講,這是一個函數(shù)表達(dá)式洛史,但是這種語法會導(dǎo)致解析兩次代碼(第一次是解析常規(guī)的ES代碼惯殊,第二次是解析傳入構(gòu)造函數(shù)中的字符串),從而影響性能也殖。不過土思,這種語法對于理解“函數(shù)是對象,函數(shù)名是指針”的概念倒是非常直觀的忆嗜。
二己儒、函數(shù)的內(nèi)部屬性
總得來說,函數(shù)的內(nèi)部屬性有三個:arguments(ES3)? this(ES3)? caller(ES5)捆毫。
(一)arguments
小知識:ES函數(shù)的參數(shù)與大多數(shù)其他語言中函數(shù)的參數(shù)有些不同闪湾,ES函數(shù)不介意傳遞的參數(shù)個數(shù)滿足符合定義函數(shù)時要求的個數(shù)。命名的參數(shù)只提供便利绩卤,并不是必需的途样。也就是說,即使你定義函數(shù)時濒憋,只接收兩個參數(shù)何暇,在調(diào)用這個函數(shù)時,可以傳入一個凛驮,兩個裆站,三個甚至不傳遞參數(shù),這是因?yàn)椋珽S函數(shù)中的參數(shù)在內(nèi)部是用一個數(shù)組來表示的遏插,函數(shù)接收到的只是這個數(shù)組捂贿,并不關(guān)心數(shù)組中包含哪些參數(shù)(如果有參數(shù)的話)纠修。
1胳嘲、參數(shù)個數(shù)任意(沒有傳遞值的命名參數(shù)被自動賦予undefined)
arguments是一個類數(shù)組對象(因?yàn)榭梢允褂梅嚼ㄌ栒Z法訪問它的每一個元素,即第一個元素arguments[0]扣草,第二個元素arguments[1]等了牛,但是,它只是與數(shù)組類似辰妙,并不是一個Array實(shí)例)鹰祸,它的作用是存儲傳入函數(shù)的所有參數(shù)。
function a(){
alert(arguments.length);
}
a ( "string" , 2 );? ? ? ? ? ? ? //2
a (2 );? ? ? ? ? ? ? ? ? ? ? ? ? ? //1
a ( );? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //0
我們可以根據(jù)參數(shù)個數(shù)不同來實(shí)現(xiàn)不同的功能:
function a(){
if ( arguments.length == 1 ){
alert(arguments[0] + 10 );
}
else if ( arguments.length == 2 ){
alert(arguments[0] + arguments[1] );
}
}
a(10);? ? ? ? ? ? //20
a( 30 , 20);? ? //50
2密浑、arguments可以和命名參數(shù)一起使用
function a( num1 ,num2 ){
if ( arguments.length == 1 ){
alert( num1 + 10 );
}
else if ( arguments.length == 2 ){
alert(arguments[0] + num2 );
}
}
上述代碼中蛙婴,num1和arguments[0]的值相同,所以可以互換著使用尔破。
記住一點(diǎn)街图,ES中所有函數(shù)的參數(shù)都是按值傳遞的,也就是說懒构,把函數(shù)外部的值復(fù)制給函數(shù)內(nèi)部的參數(shù)餐济,就和把值從一個變量復(fù)制到另一個變量一樣〉ň纾基本類型值的傳遞如同基本類型變量的賦值一樣(被傳遞的值會被復(fù)制給一個局部變量絮姆,即命名參數(shù),也可以說是arguments對象中的一個元素)秩霍,引用類型值的傳遞就如同引用類型變量的賦值一樣(把這個值在內(nèi)存中的地址復(fù)制給一個局部變量篙悯,因此這個局部變量的變化會反映在函數(shù)的外部P71)。
3铃绒、arguments.callee
callee是arguments的一個很重要的屬性鸽照,該屬性是一個指針,指向擁有這個arguments對象的函數(shù)匿垄。
function factorial ( num ){
if( num <=1){
return 1;
}else{
return num * factorial( num -1 );
}
}
var trueFactorial = factorial;
factorial = function (){
? ? return 0;
};
alert(trueFactorial (5));? ? ? //0
alert(factorial (5));? ? ? ? ? ? //0
這是一個階乘函數(shù)移宅,但是這個函數(shù)的執(zhí)行與函數(shù)名factorial緊緊耦合,當(dāng)函數(shù)名修改成trueFactorial 之后椿疗,調(diào)用trueFactorial ()函數(shù)就會返回0漏峰。是因?yàn)椋兞縯rueFactorial 獲得了factorial的值届榄,實(shí)際上是在另一個位置上保存了一個函數(shù)的指針浅乔。然后又將一個簡單的返回0的函數(shù)賦值給factorial變量。? 為了消除這種耦合,我們可以使用arguments.callee靖苇。如下席噩,這樣便可以解除函數(shù)體內(nèi)的代碼與函數(shù)名的耦合狀態(tài),trueFactorial() 可以正常的計(jì)算階乘贤壁;至于factorial() 現(xiàn)在只是個返回0 的函數(shù)悼枢。
function factorial ( num ){
if( num <=1){
return 1;
}else{
return num *arguments.callee( num -1 );
}
}
var trueFactorial = factorial;
factorial = function (){
return 0;
};
alert(trueFactorial (5));? ? ? //120
alert(factorial (5));? ? ? ? ? ? ? //0
4、arguments.caller
見(三)caller()介紹脾拆。
為了實(shí)現(xiàn)更松散的耦合馒索,ES5定義的arguments.callee.caller,定義這個屬性是為了區(qū)分arguments.caller和函數(shù)的caller的區(qū)別名船,arguments.caller嚴(yán)格模式下會報錯绰上,非嚴(yán)格模式下等于undefined。
(二)this
this的行為與Java和C#中的this類似渠驼,this引用的是函數(shù)執(zhí)行的環(huán)境對象蜈块。
(三)caller
這個是ES5新定義的屬性,這個屬性保存調(diào)用當(dāng)前函數(shù)的函數(shù)的引用
因?yàn)閛uter()調(diào)用了inner()迷扇,所以inner.caller指向outer()百揭。
嚴(yán)格模式下,不能為函數(shù)caller屬性賦值谋梭。
三信峻、函數(shù)的屬性和方法
函數(shù)有兩個屬性:length prototype
函數(shù)有三個方法:call()? apply()? bind()--ES5
(一)屬性
1、length
length屬性表示函數(shù)希望接收的命名參數(shù)的個數(shù)
function a(num){
? alert(num);
}
function b(num1,num2){
? return num1+num2;
}
function c(){
? alert("num");
}
alert(a.length);? ? //1
alert(b.length);? ? //2
alert(c.length);? ? //0
2瓮床、prototype
(二)方法
1盹舞、call()和apply()
每個函數(shù)自身都有兩個方法:apply()和call()。這兩個方法都是在特定的作用域內(nèi)調(diào)用函數(shù)隘庄,也就是修改函數(shù)體內(nèi)this對象的指向踢步。他倆的區(qū)別是接收參數(shù)的方式略有不同。(先講如何使用)
apply()接收兩個參數(shù)丑掺,一個是在其中運(yùn)行函數(shù)的作用域获印,另一個是參數(shù)數(shù)組;
call()一樣地接收兩個參數(shù)街州,一個是在其中運(yùn)行函數(shù)的作用域兼丰,另一個是枚舉出所有的參數(shù);
function a(num1,num2){
? return num1+num2;
}
function callA1(num1,num2){
? return a.apply(this,arguments);? ? //arguments是一個類數(shù)組對象
}
function callA2(num1,num2){
? return a.apply(this,[num1,num2]);
}
function callA3(num1,num2){
? return a.call(this,num1,num2);
}
alert(callA1(10,10));? ? //20
alert(callA2(10,10));? ? //20
alert(callA3(10,10));? ? //20
(再來說一下他們的強(qiáng)大之處)
前文提到過唆缴,他們的作用是修改函數(shù)體內(nèi)的this指向鳍征,也就是他們可以動態(tài)擴(kuò)展函數(shù)運(yùn)行的作用域,這也是this四種綁定方式之顯性綁定原則面徽。
使用call()和apply()來擴(kuò)充作用域的好處是對象不需要與方法有任何的耦合關(guān)系艳丛。
2匣掸、bind()
當(dāng)在函數(shù)f()上調(diào)用bind()方法并傳入一個對象o作為參數(shù),這個方法將返回一個新函數(shù)氮双。(以函數(shù)調(diào)用的方式)調(diào)用新的函數(shù)將會把原始的函數(shù)f()當(dāng)做o的方法來調(diào)用碰酝。
function f ( y ) {
return this.x+y;
}
var o={
x:1;
}
var g = f.bind(o);
g(2)? ? //3
bind()會創(chuàng)建一個實(shí)例,其this值會被綁定到bind()函數(shù)的值戴差,this的四種綁定里面這個叫做顯示綁定之硬綁定