函數(shù)就是一段可以反復(fù)調(diào)用的代碼塊。
函數(shù)使用function關(guān)鍵字來(lái)定義,還包括一個(gè)稱(chēng)為形參(parameter)的標(biāo)識(shí)符列表,這些參數(shù)在函數(shù)體內(nèi)像局部變量一樣工作。
函數(shù)調(diào)用會(huì)為形參提供實(shí)參的值。函數(shù)使用它們實(shí)參的值來(lái)計(jì)算返回值,稱(chēng)為該函數(shù)調(diào)用表達(dá)式的值。
除了實(shí)參之外,每次調(diào)用還會(huì)擁有另一個(gè)值---本次調(diào)用的上下文---這就是this關(guān)鍵字的值。
如果函數(shù)掛載在一個(gè)對(duì)象上,作為對(duì)象的一個(gè)屬性,就稱(chēng)它為對(duì)象的方法。
JavaScript的函數(shù)可以嵌套在其他函數(shù)中定義,這樣它們就可以訪問(wèn)它們被定義時(shí)所處的作用域中的任何變量,這就是JavaScript的閉包。
1、函數(shù)定義(聲明)
JavaScript有三種方法,可以定義一個(gè)函數(shù)。
(1)function命令
function name() {}
name是函數(shù)名稱(chēng)標(biāo)識(shí)符。函數(shù)名稱(chēng)是函數(shù)聲明語(yǔ)句必需的部分。不過(guò)對(duì)于函數(shù)表達(dá)式來(lái)說(shuō),名稱(chēng)是可選的:如果存在,該名字只存在于函數(shù)體內(nèi),并指向該函數(shù)對(duì)象本身。
圓括號(hào):圓括號(hào)內(nèi)可放置0個(gè)或多個(gè)用逗號(hào)隔開(kāi)的標(biāo)識(shí)符組成的列表,這些標(biāo)識(shí)符就是函數(shù)的參數(shù)名稱(chēng)。
花括號(hào):可包含0條或多條JavaScript語(yǔ)句。這些語(yǔ)句構(gòu)成了函數(shù)體。一旦調(diào)用函數(shù),就會(huì)執(zhí)行這些語(yǔ)句。
(2)函數(shù)表達(dá)式
var f = function(x){
console.log(x);
}
采用函數(shù)表達(dá)式聲明函數(shù)時(shí),function命令后面不帶有函數(shù)名。如果加上函數(shù)名,該函數(shù)名只在函數(shù)體內(nèi)部有效,在函數(shù)體外部無(wú)效。
(3)Function()
函數(shù)定義還可以通過(guò)Function()構(gòu)造函數(shù)來(lái)定義
var f=new Function('x','y','return x+y');
等價(jià)于
var f=function(x,y){
return x+y;
}
除了最后一個(gè)參數(shù)是函數(shù)體外,前面的其他參數(shù)都是函數(shù)的形參。如果函數(shù)不包含任何參數(shù),只須給構(gòu)造函數(shù)簡(jiǎn)單的傳入一個(gè)字符串---函數(shù)體---即可。
不過(guò),F(xiàn)unction()構(gòu)造函數(shù)在實(shí)際編程中很少會(huì)用到。
注意點(diǎn):
如果同一個(gè)函數(shù)被多次定義(聲明),后面的定義(聲明)就會(huì)覆蓋前面的定義(聲明)
function f(){
console.log(1);
}
f() //1
function f(){
console.log(2);
}
f() //2
函數(shù)可以調(diào)用自身,這就是遞歸(recursion)
function f(x){
if(x>2){
console.log(x);
return f(x-1);
}else{
return 1;
}
}
f(4);
// 4
//3
不能再條件語(yǔ)句中聲明函數(shù)
2、函數(shù)命名
任何合法的JavaScript標(biāo)識(shí)符都可以用做一個(gè)函數(shù)的名稱(chēng)。函數(shù)名稱(chēng)通常是動(dòng)詞或以動(dòng)詞為前綴的詞組。
通常函數(shù)名的第一個(gè)字符為小寫(xiě)。當(dāng)函數(shù)名包含多個(gè)單詞時(shí),可采取下劃線法,比如:like_this();也可以采取駝峰法,也就是除了第一個(gè)單詞之外的單詞首字母使用大寫(xiě)字母,比如:likeThis();
3、被提前
就像變量的“被提前”一樣,函數(shù)聲明語(yǔ)句也會(huì)“被提前”到外部腳本或外部函數(shù)作用域的頂部,所以以這種方式聲明的函數(shù),可以被在它定義之前出現(xiàn)的代碼所調(diào)用。
上面的代碼不會(huì)報(bào)錯(cuò)。
注意:以表達(dá)式定義的函數(shù)并沒(méi)有“被提前”。
f();
var f = function (){};
// TypeError: f is not a function
變量其實(shí)是分為聲明,賦值兩部分的,上面的代碼等同于下面的形式
var f;
f();
f = function() {};
調(diào)用f的時(shí)候,f只是被聲明了,還沒(méi)有被賦值,等于undefined,所以會(huì)報(bào)錯(cuò)。
4、嵌套函數(shù)
在JavaScript中,函數(shù)可以嵌套在其他函數(shù)里。
function go(){
function play(){}
return play();
}
5、函數(shù)調(diào)用
構(gòu)成函數(shù)主體的JavaScript代碼在定義時(shí)并不會(huì)執(zhí)行,只有調(diào)用該函數(shù),它們才會(huì)執(zhí)行。有4種方式調(diào)用JavaScript函數(shù):
- 作為函數(shù)
- 作為方法
- 作為構(gòu)造函數(shù)
- 通過(guò)它們的call()和apply()方法間接調(diào)用
5.1函數(shù)調(diào)用
f();
5.2方法調(diào)用
o.f=funciton(){}
o.f();
5.3構(gòu)造函數(shù)調(diào)用
如果函數(shù)或者方法調(diào)用之前帶有關(guān)鍵字new,它就構(gòu)成構(gòu)造函數(shù)調(diào)用。
凡是沒(méi)有形參的構(gòu)造函數(shù)調(diào)用都可以省略圓括號(hào)。
var o=new Object();
var o=new Object;
5.4間接調(diào)用
6、函數(shù)的實(shí)參和形參
可選形參
當(dāng)調(diào)用函數(shù)的時(shí)候傳入的實(shí)參比函數(shù)聲明時(shí)指定的形參個(gè)數(shù)要少,剩下的形參都將設(shè)置為undefined值。
為了保持好的適應(yīng)性,一般應(yīng)當(dāng)給參數(shù)賦予一個(gè)合理的默認(rèn)值。
function go(x,y){
x = x || 1;
y = y || 2;
}
注意:當(dāng)用這種可選實(shí)參來(lái)實(shí)現(xiàn)函數(shù)時(shí),需要將可選實(shí)參放在實(shí)參列表的最后。那些調(diào)用你的函數(shù)的程序員是沒(méi)法省略第一個(gè)參數(shù)并傳入第二個(gè)實(shí)參的。
可變長(zhǎng)的實(shí)參列表:實(shí)參對(duì)象
當(dāng)調(diào)用函數(shù)時(shí),傳入的實(shí)參個(gè)數(shù)超過(guò)函數(shù)定義時(shí)的形參個(gè)數(shù)時(shí),是沒(méi)有辦法直接獲得未命名值的引用。
這時(shí),標(biāo)識(shí)符arguments出現(xiàn)了,其指向?qū)崊?duì)象的引用,實(shí)參對(duì)象是一個(gè)類(lèi)數(shù)組對(duì)象,可以通過(guò)數(shù)字下標(biāo)來(lái)訪問(wèn)傳入函數(shù)的實(shí)參值,而不用非要通過(guò)名字來(lái)得到實(shí)參。
function go(x){
console.log(arguments[0]);
console.log(arguments[1]);
}
go(1,2);
//1
//2
arguments有一個(gè)length屬性,用以標(biāo)識(shí)其所包含元素的個(gè)數(shù)。
function f(x){
console.log(arguments.length);
}
f(1,2) // 2
注意:arguments并不是真正的數(shù)組,它是一個(gè)實(shí)參對(duì)象。每個(gè)實(shí)參對(duì)象都包含以數(shù)字為索引的一組元素以及l(fā)ength屬性。
通過(guò)實(shí)參名字來(lái)修改實(shí)參值的話,通過(guò)arguments[]數(shù)組也可以獲取到更改后的值。
function f(x){
console.log(x); // 1
arguments[0]=null;
console.log(x); // null
}
f(1);
在上面的例子中,arguments[0]和x指代同一個(gè)值,修改其中一個(gè)的值會(huì)影響到另一個(gè)。
注意:如果有同名的參數(shù),則取最后出現(xiàn)的那個(gè)值。
function f(x,x){
console.log(x);
}
f(1,2) // 2
callee和caller屬性arguments對(duì)象帶有一個(gè)callee屬性,返回它所對(duì)應(yīng)的原函數(shù)。
7、將對(duì)象屬性用做實(shí)參
當(dāng)一個(gè)函數(shù)包含超過(guò)三個(gè)形參時(shí),要記住調(diào)用函數(shù)中實(shí)參的正確順序是件讓人頭疼的事。不過(guò),我們可以通過(guò)名/值對(duì)的形式傳入?yún)?shù),這樣就無(wú)法管參數(shù)的順序了。
function f(params){
console.log(params.name);
}
f({name:'a'})
8、作為值的函數(shù)
在JavaScript中,我們可以將函數(shù)賦值給變量。
function f(){}
var a=f;
9、函數(shù)作用域
作用域(scope)指的是變量存在的范圍。Javascript只有兩種作用域:一種是全局作用域,變量在整個(gè)程序中一直存在,所有地方都可以讀??;另一種是函數(shù)作用域,變量只在函數(shù)內(nèi)部存在。
在函數(shù)外部聲明的變量就是全局變量(global variable),它可以在函數(shù)內(nèi)部讀取。
var a=1;
function f(){
console.log(a)
}
f() //1
上面的代碼中,函數(shù)f內(nèi)部可以讀取全局變量a。
在函數(shù)內(nèi)部定義的變量,外部無(wú)法讀取,稱(chēng)為“局部變量”(local variable)。
function f(){
var a=1;
}
v //ReferenceError: v is not defined
上面代碼中,變量v在函數(shù)內(nèi)部定義,所以是一個(gè)局部變量,函數(shù)之外就無(wú)法讀取。
函數(shù)內(nèi)部定義的變量,會(huì)在該作用域內(nèi)覆蓋同名全局變量。
var a=1;
function f(){
var a=2;
console.log(a);
}
f() //2
a //1
注意:對(duì)于var命令來(lái)說(shuō),局部變量只能在函數(shù)內(nèi)部聲明,在其他區(qū)塊中聲明,一律都是全局變量。
函數(shù)的執(zhí)行依賴(lài)于變量作用域,這個(gè)作用域是在函數(shù)定義時(shí)決定的,而不是函數(shù)調(diào)用時(shí)決定的。
10、函數(shù)內(nèi)部的變量提升
與全局作用域一樣,函數(shù)作用域內(nèi)部也會(huì)產(chǎn)生“變量提升”現(xiàn)象。var命令聲明的變量,不管在什么位置,變量聲明都會(huì)被提升到函數(shù)體的頭部。
function f(x){
if(x>10){
var a = x -1;
}
}
//等同于
function f(x){
var a;
if(x>10){
a = x - 1;
}
}
11、函數(shù)屬性、方法和構(gòu)造函數(shù)
name屬性
name屬性返回緊跟在function關(guān)鍵字之后的那個(gè)函數(shù)名。
function f(){}
f.name //f
length屬性函數(shù)的length屬性是只讀屬性,代表函數(shù)形參的數(shù)量,也就是在函數(shù)定義時(shí)給出的形參個(gè)數(shù)。
function f(x,y){}
f.length //2
prototype屬性
每一個(gè)函數(shù)都包含一個(gè)prototype屬性,這個(gè)屬性指向一個(gè)對(duì)象的引用,這個(gè)對(duì)象稱(chēng)做“
原型對(duì)象”(prototype object)。
call()方法和apply()方法
call()
語(yǔ)法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定義:調(diào)用一個(gè)對(duì)象的一個(gè)方法,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象。
說(shuō)明: call 方法可以用來(lái)代替另一個(gè)對(duì)象調(diào)用一個(gè)方法。call 方法可將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象。
apply()
語(yǔ)法:apply([thisObj[,argArray]]) 定義:應(yīng)用某一對(duì)象的一個(gè)方法,用另一個(gè)對(duì)象替換當(dāng)前對(duì)象。
說(shuō)明: 如果 argArray 不是一個(gè)有效的數(shù)組或者不是 arguments 對(duì)象,那么將導(dǎo)致一個(gè) TypeError。 如果沒(méi)有提供 argArray 和 thisObj 任何一個(gè)參數(shù),那么 Global 對(duì)象將被用作 thisObj, 并且無(wú)法被傳遞任何參數(shù)。
bind()方法
bind()方法是在ECMAScript 5中新增的方法。
toString()方法
函數(shù)的toString方法返回函數(shù)的源碼。
function f(){
return 1;
}
f.toString()
//function f(){
// return 1;
//}
12、閉包
JavaScript的函數(shù)可以嵌套在其他函數(shù)中定義,這樣它們就可以訪問(wèn)它們被定義時(shí)所處的作用域中的任何變量,這就是JavaScript的閉包。
閉包的最大用處有兩個(gè),一個(gè)是可以讀取函數(shù)內(nèi)部的變量,另一個(gè)就是讓這些變量始終保持在內(nèi)存中,即閉包可以使得它誕生環(huán)境一直存在。
function f(a){
return function(){
return a++;
};
}
var c=f(1);
console.log(c()); //1
console.log(c()); //2
console.log(c()); //3
閉包的另一個(gè)用處,是封裝對(duì)象的私有屬性和私有方法。
13、立即調(diào)用的函數(shù)表達(dá)式(IIFE)
在Javascript中,一對(duì)圓括號(hào)()是一種運(yùn)算符,跟在函數(shù)名之后,表示調(diào)用該函數(shù)。
(function(){
statement
}())
上面的函數(shù)會(huì)立即調(diào)用。
注意:上面代碼的圓括號(hào)的用法,function之前的左圓括號(hào)是必需的,因?yàn)槿绻粚?xiě)這個(gè)左圓括號(hào),JavaScript解釋器會(huì)試圖將關(guān)鍵字function解析為函數(shù)聲明語(yǔ)句。而使用圓括號(hào),JavaScript解釋器才會(huì)正確地將其解析為函數(shù)定義表達(dá)式。
當(dāng)然,下面的方法也會(huì)以表達(dá)式來(lái)處理函數(shù)定義的方法。
!function(){}();
~function(){}();
-function(){}();
+function(){}();
通常情況下,只對(duì)匿名函數(shù)使用這種“立即執(zhí)行的函數(shù)表達(dá)式”。它的目的有兩個(gè):
一是不必為函數(shù)命名,避免了污染全局變量;
二是IIFE內(nèi)部形成了一個(gè)單獨(dú)的作用域,可以封裝一些外部無(wú)法讀取的私有變量。
14、eval命令
eval命令的作用是,將字符串當(dāng)作語(yǔ)句執(zhí)行。
eval('var a=1');
a //1
eval沒(méi)有自己的作用域,都在當(dāng)前作用域內(nèi)執(zhí)行
JavaScript規(guī)定,如果使用嚴(yán)格模式,eval內(nèi)部聲明的變量,不會(huì)影響到外部作用域。
(function(){
'use strict';
eval('var a=1');
console.log(a); //ReferenceError: a is not defined
})();
更多建議: