JavaScript中的函數(shù)是一段代碼,被定義一次,但是可以調(diào)用或執(zhí)行多次。函數(shù)定義的時候會包含一個函數(shù)形參的標識符列表,這些參數(shù)在函數(shù)內(nèi)部像局部變量一樣工作。函數(shù)調(diào)用時會為形參提供實參的值。函數(shù)調(diào)用時還有一個值,就是本次調(diào)用的上下文,就是this關(guān)鍵字指向的值。
函數(shù)是對象,我們可以把它賦值給變量或者作為函數(shù)的參數(shù)。此外,我們還可以給他設(shè)置屬性和方法。
二、函數(shù)定義
1、函數(shù)聲明語句
1 function x(a,b){2 return a+b;3 }
2、函數(shù)定義表達式
1 var x = function (a,b) {2 return a + b;3 }4 //自調(diào)用函數(shù)5 var y = (function (a, b) {6 return a + b;7 }(1, 2));8 console.log(y);//3
沒有return語句的函數(shù)的返回值時undefined;同時函數(shù)可以嵌套在另個函數(shù)里面,內(nèi)部函數(shù)可以讀取外部函數(shù)的局部變量。
三、函數(shù)調(diào)用的方法
1、作為函數(shù)
1 //函數(shù)調(diào)用2 function X(){3 return 0;4 }5 var x=X();//0
函數(shù)調(diào)用的this指向全局對象,嚴格模式下指向undefined,如果該函數(shù)有嵌套函數(shù),嵌套函數(shù)的this也指向全局對象(undefined)。
2、方法調(diào)用
1 function X(){ 2 var self=this; 3 function myfun(){ 4 console.log(self===o);//true 5 console.log(this===global);//true 6 } 7 myfun(); 8 return 0; 9 }10 var o={};11 o.m=X;12 //方法調(diào)用13 var x=o.m();//x
方法調(diào)用的this指向調(diào)用方法的對象。內(nèi)部函數(shù)要訪問外部函數(shù)的this,需要將this保存在一個對象中。
3、構(gòu)造函數(shù)調(diào)用
調(diào)用構(gòu)造函數(shù)通常使用new關(guān)鍵字,構(gòu)造函數(shù)的調(diào)用對象(this)就是這個新創(chuàng)建的。使用new 關(guān)鍵字調(diào)用構(gòu)造函數(shù)時,先創(chuàng)建一個新的空對象,對象的原型指向構(gòu)造函數(shù)的prototype屬性,然后初始化這個對象并返回。
如果構(gòu)造函數(shù)沒有return或者有return但是返回的是一個原始值或者沒有指定返回值,則表達式的值是這個新建的對象,如果return返回的是一個對象,則表達式的值是這個對象。
4、間接調(diào)用
使用Function.call()和Function.apply()間接調(diào)用函數(shù),第一個參數(shù)指定調(diào)用方法的上下文(this),后面的參數(shù)指定調(diào)用方法需要的參數(shù),call()中參數(shù)作為方法的實參,apply的參數(shù)以數(shù)組的形式傳給方法的實參。
1 function x(x){2 console.log(x); 3 }4 x.call(null,'hello');//hello5 x.apply(this,['world']);//world
四、函數(shù)的形參和實參
函數(shù)定義的時候傳入的參數(shù)是形參,函數(shù)調(diào)用的時候傳入的參數(shù)是實參,形參的引用指向?qū)崊ⅰ?/span>
JavaScript中函數(shù)調(diào)用時如果實參多余形參或自動省略,實參少于形參時用undefined填補。
利用不夠的形參將使用undefined,我們可以實現(xiàn)一個參數(shù)可選的函數(shù),可選的參數(shù)應(yīng)該放在最后,當(dāng)我們不給他傳入值時應(yīng)該使用默認值。
1 //可選參數(shù)的函數(shù)2 function x(a,b){3 if(a===undefined) a=10;4 b=b||1;5 return a+b;6 }7 console.log(x()); //11
arguments是指向?qū)崊ο蟮囊茫瑢崊ο笫且粋€類數(shù)組對象??梢酝聵说男问皆L問實參(第一個實參用argumens[0],...),也可以實現(xiàn)可變長形參的函數(shù)。
比較任意個數(shù)的中的最大值;
1 //可選參數(shù)的函數(shù) 2 function x(/*...*/){ 3 var max=arguments[0]; 4 for(var i=0;i<arguments.length;i++){ 5 if(max<arguments[i]) 6 max=arguments[i]; 7 } 8 return max; 9 }10 console.log(x(1,2,3)); //3
五、實參對象callee和length屬性
callee指向當(dāng)前正在執(zhí)行的函數(shù),length代表了當(dāng)前調(diào)用函數(shù)的實參數(shù)量。
1 function X(){2 console.log(arguments.length);//03 function Y(){4 console.log(arguments.caller);//Y5 }6 Y();7 }8 X();
六、函數(shù)的屬性
1、caller
如果一個函數(shù)f
是在全局作用域內(nèi)被調(diào)用的,則f.caller為
null
,相反,如果一個函數(shù)是在另外一個函數(shù)作用域內(nèi)被調(diào)用的,則f.caller指向調(diào)用它的那個函數(shù).
該屬性的常用形式arguments.callee.caller
替代了被廢棄的 arguments.caller.
1 function X(){2 console.log(X.caller);//null3 function Y(){4 console.log(Y.caller);//X5 }6 Y();7 }8 X();
2.length屬性指明函數(shù)的形參個數(shù)。
1 function X(a,b){2 console.log(X.length);//23 }4 X();
3、prototype屬性
每個函數(shù)都有prototype屬性,該屬性指向一個對象,這個對象成為原型對象。當(dāng)時用該函數(shù)創(chuàng)建一個對象,對象會從原型對象繼承屬性。
4、call()和apply()
函數(shù)的這兩個方法使的函數(shù)可以作為其他對象的方法調(diào)用。即使對象沒有這個方法。
call()和apply()的一個參數(shù)是函數(shù)調(diào)用的上下文對象,可以是null和undefined,當(dāng)時null和undefined的時候,會用全局對象代替,當(dāng)時原始值的時候,會轉(zhuǎn)化為對應(yīng)的包裝對象。
第二個參數(shù)是函數(shù)的調(diào)用的參數(shù)是要傳入的參數(shù)。apply()把函數(shù)要傳入的參數(shù)以數(shù)組的形式傳入。
5、bind()
bind()用來將一個函數(shù)f綁定到一個對象o,并返回一個新的函數(shù),這個新的函數(shù)會把f作為o的方法調(diào)用。
bind()可以接受額外的參數(shù),額外的實參會作為函數(shù)的參數(shù)傳入;
1 function add(x,y){return x+y};2 var x=add.bind(null,1,2);//把函數(shù)綁定到null,x綁定1;y綁定23 console.log(x());//3
當(dāng)綁定完的返回的新函數(shù)length屬性為原函數(shù)的length減去綁定的實參個數(shù)。所以上面的函數(shù)x的length為0;
當(dāng)綁定的函數(shù)作為構(gòu)造函數(shù)時,函數(shù)無prototype屬性,函數(shù)的this會忽略綁定的對象。創(chuàng)建的對象的原型指向原來函數(shù)的prototype,使用instanceof驗證時,綁定函數(shù)和原函數(shù)沒有區(qū)別。
1 function X(a,b){2 this.a=a;3 this.b=b;4 }5 var o=Object.create({x:1});6 var x=X.bind(o,1,2);//把函數(shù)綁定到o,a=1;b=27 var z=new x();8 console.log(z instanceof x);//true9 console.log(z instanceof X);//true
6、toString()
大多數(shù)函數(shù)的toString()返回源碼,內(nèi)置函數(shù)的toString()返回[native code].
七、函數(shù)的使用
JavaScript中的函數(shù)是一個值,可以賦值給變量、對象的屬性、數(shù)組的元素,還可以作為函數(shù)的參數(shù)。數(shù)組的sort()方法的參數(shù)就是一個函數(shù),允許他設(shè)置數(shù)組的元素的排序規(guī)則。
函數(shù)是一個對象,我們可以給函數(shù)定義屬性。
1 //用函數(shù)的屬性保存一個值,這樣可以減少全局變量的使用2 XX.n=0;3 function XX(){4 return XX.n++;5 }
作為命名空間的函數(shù):函數(shù)內(nèi)部定義的變量只在函數(shù)內(nèi)部有效,而不會污染全局命名空間。模塊的定義常用到這個方法,因為模塊會在不同的程序中運行,我們不能保證模塊中定義的變量是否在程序中已經(jīng)存在,我們可以把變量定義在一個函數(shù)內(nèi)部,這樣變量的作用域就不是全局的。
1 (function(){2 //moudle中的代碼3 }())
八、閉包
閉包:函數(shù)的變量可以隱藏于作用域鏈之內(nèi),因此看起來像函數(shù)把變量包裹起來了一樣。
JavaScript采用詞法作用域,函數(shù)的執(zhí)行依賴變量的作用域,這個作用域?qū)嵲诤瘮?shù)定義的時決定的,而不是在函數(shù)調(diào)用時決定的。為了實現(xiàn)這種詞法作用域,JavaScript的函數(shù)不僅包含代碼的邏輯部分,還要引用當(dāng)前的作用域鏈。
1 var scope="global scope"; 2 function f(){ 3 var scope="local scope"; 4 function fun(){ 5 return scope; 6 } 7 return fun; 8 } 9 var x=f()();10 console.log(x);//"local scope"
函數(shù)定義的時候會保存一個作用域鏈,當(dāng)函數(shù)調(diào)用的時候會創(chuàng)建一個保存函數(shù)局部變量的對象,把對象添加到作用域鏈上。當(dāng)函數(shù)返回時會從作用域鏈刪除該對象。當(dāng)函數(shù)內(nèi)部存在嵌套函數(shù),嵌套函數(shù)的作用域鏈指向該對象,如果嵌套函數(shù)在外部函數(shù)中保存下來,外部函數(shù)返回的時候變量綁定對象會被回收,但是如果嵌套函數(shù)被返回或者存儲在某處的屬性里,則會有一個外部引用指向該函數(shù),他的作用域鏈上的綁定對象則不會被回收。當(dāng)我們調(diào)用函數(shù)時會從函數(shù)的作用域上查找變量,所以上面我們訪問到的還是局部變量。
我們可以利用閉包來實現(xiàn)變量的私有。
1 function fun(){ 2 var n=10; 3 return { 4 add:function f(){ 5 return n++; 6 }, 7 set: function g(){ 8 n=0; 9 }10 }11 }12 var o=fun();13 o.add();//1014 o.set15 o.add();//0
當(dāng)fun()方法調(diào)用返回后,只能通過函數(shù)的嵌套函數(shù)才能訪問到局部變量n,這個變量就變成了一個私有變量。兩個嵌套函數(shù)(閉包)共享變量n.
很多時候我們不希望私有變量共享。
1 function fun(){ 2 var arr=[],i=0; 3 for(i;i<10;i++){ 4 arr[i]=function(){ 5 return i; 6 } 7 } 8 return arr; 9 }10 var a=fun();11 a.forEach((ele)=>{12 console.log(ele());//結(jié)果全是10;13 });
上面的代碼中我們創(chuàng)建了10個閉包。10個閉包共享變量i;
1 function fun(){ 2 var arr=[],i=0; 3 for(i;i<10;i++){ 4 arr[i]=(function(x){ 5 function f(){ 6 return x; 7 } 8 return f; 9 }(i));10 }11 return arr;12 }13 var a=fun();14 a.forEach((ele)=>{15 console.log(ele());//結(jié)果全是0....9;16 });
這個例子中我們把i作為參數(shù)傳給不同的函數(shù)的形參x,然后讓閉包中的x不共享。
當(dāng)使用閉包是無法訪問到外部函數(shù)的this和arguments。只能將其賦值給一個變量才能訪問到。
九、Function構(gòu)造函數(shù)
1 var z=new Function('x','y',"return x+y");
上面的函數(shù)等價于var z=function(x,y){return x+y};