標籤:color 使用 資料 os cti for
每個函數都是Function類型的執行個體,而且都與其他參考型別一樣具有屬性和方法。
由於函數是對象,因此函數名實際上也是一個指向函數對象的指標,不會與某個函數綁定。
函數的聲明有以下三種形式:
function sum(num1,num2){return num1+num2;}//利用函式宣告文法定義
var sum=function(num1,num2){return num1+num2;}//利用函數運算式定義
var sum=new Function("num1","num2","return num1+num2");//利用Function建構函式定義
如果利用第三種方法定義函數,會導致解析兩次代碼(第一次是解析常規ECMAScript代碼,第二次是解析傳入建構函式中的字串),從而影響效能,因此並不推薦。
而在前面兩種函數定義形式當中,解析器會率先讀取函式宣告,並使其在執行任何代碼之前可用,至於函數運算式,則必須等到解析器執行到它所在的程式碼,才會真正被解釋執行。所以如果使用函數運算式來定義函數,則在代碼執行到該行之前便對其進行調用就會報錯,而如果是利用函式宣告定義函數則不會有類似錯誤發生。
另外,也可以同時使用函式宣告和函數運算式,例如var sum=function sum(){}。不過這種文法在Safari當中會報錯。
由於函數名僅僅是指向函數的指標,因此函數名與包含對象指標的其他變數沒有什麼不同,換句話說就是一個函數可能會有多個名字。
function sum(num1,num2){return num1+num2;}
alert(sum(10,10));//20
var anotherSum=sum;//使用不帶圓括弧的函數名是訪問函數指標,而非調用函數
alert(anotherSum(10,10));//20
sum=null;//此處將sum設定為null,讓它與函數斷絕關係
alert(anotherSum(10,10));//20,此處仍可正常調用證明sum只是一個指標而不能代表函數
將函數名想象成指標,也有助於理解為什麼ECMAScript中沒有函數重載的概念。
在ECMAScript中如果聲明了兩個同名函數,結果就會是後面的函數覆蓋前面的函數。
function addSomeNumber(num){return num+100;}
function addSomeNumber(num){return num+200;}
var result=addSomeNumber(100);//300
因為ECMAScript中的函數名本身就是變數,所以函數也可以作為值來使用。
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,"Nicholas");
alert(result2);//"Hello,Nicholas"
這裡的callSomeFunction()函數是通用的,即無論第一個參數中傳遞進來的是什麼函數,它都會返回執行第一個參數後的結果。
當然,也可以從一個函數中返回另一個函數。
function createComparisonFunction(propertyName){
return function(object1,object2){
var value1=object1[propertyName];
var value2=object2[propertyName];
if(value1<value2){return -1;}
else if(value1>value2){return 1;}
else{return 0;}
};
}
var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
alert(data[0].name);//Nicholas
data.sort(createComparisonFunction("age"));
alert(data[0].name);//Zachary
在函數的內部,有兩個特殊的對象:arguments和this。
其中arguments是一個類數組對象,包含著傳入函數中的所有參數,該對象還有一個名叫callee的屬性,該屬性是一個指標,指向擁有這個arguments對象的函數。
利用該屬性可以解除函數體內代碼的函數名的耦合狀態,下面以一個階乘函數為例:
function factorial(num){if(num<=1){return 1;}else{return num*factorial(num-1);}}
//此處這個函數的執行與函數名緊緊的耦合在了一起
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
函數內部的另一個特殊對象是this,this引用的是函資料以執行的環境對象。
window.color="red";
var o={color:"blue"};
function sayColor(){alert(this.color);}
sayColor();//"red"
o.sayColor=sayColor;
o.sayColor();//"blue"
ECMAScript5也正常化了另一個函數對象的屬性:caller。除了Opera早期版本不支援,其他瀏覽器都支援這個ECMAScript3中並沒有定義的屬性。
這個屬性中儲存著調用當前函數的函數的引用,如果是在全域範圍中調用當前函數,它的值為null。
function outer(){inner();}
function inner(){alert(inner.caller);}
outer();//以上代碼會導致警示框當中顯示outer()函數的原始碼
因為outer()調用了inner(),所以inner.caller就指向了outer(),為了實現更鬆散的耦合,也可以通過arguments.callee.caller來訪問相同的資訊。
function outer(){inner();}
function inner(){alert(arguments.callee.caller);}
outer();//IE、Firefox、Chrome、Safari、Opera9.6+都支援caller屬性
當函數在strict 模式下運行時,訪問arguments.callee會導致錯誤,且不能為函數的caller屬性賦值,否則會導致錯誤。
ECMAScript5還定義了arguments.caller屬性,但在strict 模式下會導致錯誤,在非strict 模式下這個屬性始終是undefined。
定義這個屬性是為了區分arguments.callee和函數的caller屬性,這些變化都是為了加強語言的安全性。
ECMAScript中的函數是對象,每個函數都包含兩個屬性:length和prototype。
其中,length屬性工作表示函數希望接受的具名引數的個數。
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
對於ECMAScript中的參考型別而言,prototype是儲存它們所有執行個體方法的真正所在,諸如toString()和valueOf()等方法實際上都儲存在prototype名下,只不過是通過各自對象的執行個體訪問罷了。在ECMAScript5總prototype屬性是不可枚舉的,因此使用for-in無法發現。
ECMAScript中的每個函數都包含兩個非繼承而來的方法:apply()和call()。
這兩個方法的用途都是在特定的範圍中調用函數,實際上等於設定函數體內this對象的值。
function sum(num1,num2){return num1+num2;}
function callSum1(num1,num2){return sum.apply(this,arguments);}//傳入arguments對象
function callSum2(num1,num2){return sum.apply(this,[num1,num2]);}//傳入數組
alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
call()方法與apply()方法的作用相同,他們的區別在於接受參數的方式不同,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來。
function callSum(num1,num2){retrun sum.call(this,num1,num2);}
alert(callSum(10,10));//20
事實上,傳遞參數並非這兩個方法的用武之地,他們真正強大的地方是能夠擴充函數賴以啟動並執行範圍。
window.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
ECMAScript5中還定義了一個方法:bind()。
這個方法會建立一個函數的執行個體,其this值會被綁定到傳給bind()函數的值。
window.color="red";
var o={color:"blue"};
function sayColor(){alert(this.color);}
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue
支援bind()方法的瀏覽器有IE9+、Firefox4+、Safari5.1+、Opera12、Chrome。