函數(shù)實際上是對象绩郎。
函數(shù)名是指向函數(shù)的指針。
使用不帶圓括號的函數(shù)名是訪問函數(shù)指針翁逞,而非調(diào)用函數(shù)肋杖。
函數(shù)定義
函數(shù)聲明語法
function sum1(num1,num2){
return num1+num2;
}
函數(shù)表達式
var sum = function(num1,num2){
return num1+num2;
};
使用Function構(gòu)造函數(shù)
var sum = new Function("num1","num2","return num1+num2");
//不推薦上面的寫法,原因:這種語法會導致解析兩次代碼(第一次是解析常規(guī)ECMAScript代碼挖函,
//第二次是解析傳入構(gòu)造函數(shù)中的字符串)状植,從而影響性能。
沒有重載
因為ECMAScript函數(shù)不介意傳遞進來多少參數(shù),也不在乎傳進來參數(shù)是什么數(shù)據(jù)類型津畸。也就是說振定,即使定義的函數(shù)只接收兩個參數(shù),在調(diào)用這個函數(shù)時也未必一定要傳遞兩個參數(shù)肉拓》园福可以傳遞一個、三個甚至不傳參數(shù)帝簇。
因為ECMAScript中的參數(shù)在內(nèi)部時用一個數(shù)組來表示。在函數(shù)體內(nèi)可以通過arguments對象來訪問這個參數(shù)數(shù)組靠益,從而獲取傳遞給函數(shù)的每一個參數(shù)丧肴。
function doAdd(){
if(arguments.length == 1){
alert(arguments[0]+10);
}else if(arguments.length == 2){
alert(arguments[0]+arguments[1]);
}
}
因此,只能通過檢查傳入函數(shù)中參數(shù)的類型和數(shù)量并作出不同的反應來模仿方法的重載胧后。
函數(shù)聲明與函數(shù)表達式
解析器在向執(zhí)行環(huán)境中加在數(shù)據(jù)時芋浮,對函數(shù)聲明和函數(shù)表達式并非一視同仁。
解析器會率先讀取函數(shù)聲明壳快,并使其在執(zhí)行任何代碼之前可用(可以訪問)纸巷。
而函數(shù)表達式,則必須等到解析器執(zhí)行到它所在的代碼行眶痰,才會真正被解釋執(zhí)行瘤旨。
alert(sum(10,10));
function sum(num1,num2){
return num1+num2;
}
以上代碼可以正常運行。因為在代碼開始執(zhí)行之前竖伯,解析器已經(jīng)通過一個名為函數(shù)聲明提升(function declaration hoisting)的過程存哲,讀取并將函數(shù)聲明添加到執(zhí)行環(huán)境中。對代碼求值時七婴,JavaScript引擎在第一遍會聲明函數(shù)并將它們放到源代碼樹的頂部祟偷。
因此,即使聲明函數(shù)的代碼在調(diào)用它的代碼后面打厘,JavaScript引擎也能把函數(shù)聲明提升到頂部修肠。
若把函數(shù)聲明改為等價的函數(shù)表達式,就會在執(zhí)行期間導致錯誤户盯。
alert(sum(10,10));
var sum = function(num1,num2){
return num1+num2;
}
以上代碼會在運行期間產(chǎn)生錯誤嵌施,因為在執(zhí)行到函數(shù)所在的語句之前,變量sum中不會保存有對函數(shù)的引用莽鸭;而且艰管,由于第一行代碼就會導致“unexpected identifier”(意外標識符)錯誤,實際上也不會執(zhí)行到下一行蒋川。
作為值的函數(shù)
因為ECMAScript中的函數(shù)名本身就是變量牲芋,所以函數(shù)也可以作為值來使用。
1、可以像傳遞參數(shù)一樣把一個函數(shù)傳遞給另一個函數(shù)
2缸浦、可以將一個函數(shù)作為另一個函數(shù)的結(jié)果返回夕冲。
function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}
例如:
function add10(num){
return num+10;
}
var result1 = callSomeFunction(add10,10);
alert(result1); //20
function getGreeting(name){
return "Hello,"+name;
}
var result2 = callSomeFunction(getGreeting,"Wonder");
alert(result2); //"Hello,Wonder"
函數(shù)內(nèi)部屬性
函數(shù)內(nèi)部有兩個特殊的對象:arguments和this。
1裂逐、arguments
用途:保存函數(shù)參數(shù)歹鱼。
此對象有一個屬性叫callee,是一個指針卜高,指向擁有這個arguments對象的函數(shù)弥姻。
例如:
function factorial(num){
if(num<=1){
return 1;
}else{
return num*factorial(num-1);
}
}
//上面的代碼在函數(shù)有名字并且名字之后也不會改變的情況下,這樣定義是沒有問題掺涛。
//但是這個函數(shù)的執(zhí)行與函數(shù)名緊緊綁在了一起庭敦,為了消除這種緊密耦合的現(xiàn)象,可以像下面這樣使用arguments.callee薪缆。
function factorial(num){
if(num<=1){
return 1;
}else{
return num*argument.callee(num-1);
}
}
//重寫后的factorial()函數(shù)的函數(shù)體內(nèi)秧廉,沒有再引用函數(shù)名factorial。
//這樣不管引用函數(shù)時使用的是什么名字拣帽,都可以保證正常完成遞歸調(diào)用疼电。例如:
var trueFactorial = factorial;
factorial = function(){
return 0;
};
alert(trueFactorial(5)); //120
alert(factorial(5)); //0
2、this
this引用的是函數(shù)執(zhí)行的環(huán)境對象减拭。
windows.color="red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"Blue"
ECMAScript 5規(guī)劃了另一個函數(shù)對象屬性:caller蔽豺。這個屬性中保存著調(diào)用當前函數(shù)的函數(shù)的引用。如果在全局作用域中調(diào)用當前函數(shù)拧粪,它的值為null茫虽。
function outer(){
inner();
}
function inner(){
alert(inner.caller);
}
outer();
以上代碼會導致警告框中顯示outer()函數(shù)的源代碼。outer()調(diào)用了inner()既们,所以inner.caller指向outer()濒析。為了實現(xiàn)更松散的耦合,也可以通過arguments.callee.caller來訪問相同的信息啥纸。
function outer(){
inner();
}
function inner(){
alert(arguments.callee.caller);
}
outer();
若函數(shù)在嚴格模式下運行時,訪問arguments.callee會導致錯誤斯棒。ECMAScript 5還定義了arguments.caller屬性盾致,但是在嚴格模式下訪問它也會導致錯誤,而非嚴格模式下這個屬性適中是undefined荣暮。
定義arguments.callee屬性是為了分清arguments.caller和函數(shù)的caller屬性庭惜。
嚴格模式還有一個限制:不能為函數(shù)的caller屬性賦值,否則會導致錯誤穗酥。
函數(shù)屬性和方法
length屬性:表示函數(shù)希望接受的命名參數(shù)的個數(shù)护赊。
function sayName(name){
alert(name);
}
function sum(num1,num2){
return num1+num2;
}
function sayHi(){
alert("hi");
}
alert(sayName.length); //1
alert(sum.length); //2
alert(sayHi.length); //0
prototype屬性:保存所有實例方法的真正所在惠遏。即諸如toString()和valueOf()等方法都保存在prototype名下,通過各自對象的實例訪問骏啰。
每個函數(shù)都包含兩個非繼承而來的方法:apply()和call()
用途:在特定的作用域中調(diào)用函數(shù)节吮,實際上等于設置函數(shù)體內(nèi)this對象的值。
apply()方法
param:
param1——在其中運行函數(shù)的作用域
param2——參數(shù)數(shù)組(可以是Array實例判耕,也可以是arguments對象透绩。)
function sum(num1,num2){
return num1+num2;
}
function callSum1(num1,num2){
return sum.apply(this,arguments);
}
function callSum1(num1,num2){
return sum.apply(this,[num1,num2];
}
callSum1(10,10); //20
callSum2(10,10); //20
call()方法
param:
param1——在其中運行函數(shù)的作用域
param2——其余參數(shù)都直接傳遞給函數(shù)。即在使用call()方法時壁熄,傳遞給函數(shù)的參數(shù)必須逐個列舉出來帚豪。
function sum(num1,num2){
return num1+num2;
}
function callSum(num1,num2){
return sum.call(this,num1,num2);
}
callSum(10,10); //20
實際上,apply()和call()真正強大的地方是能擴充函數(shù)賴以運行的作用域草丧。
windows.color = "red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
ECMAScript 5還定義了一個方法:bind()狸臣。這個方法會創(chuàng)建一個函數(shù)的實例,其this值會被綁定到傳給bind()函數(shù)的值方仿。
windows.color = "red";
var o = {color:"blue"};
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue