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