JS基礎知識回顧:參考型別(四)

來源:互聯網
上載者:User

標籤: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。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.