1. 程式人生 > >js函式小結

js函式小結

js函式小結

js函式小結

js函式的定義

function functionName(parameters) {
  執行的程式碼
}
  • 示例如下:
function myFunction(a, b) {
    return a * b;
}
  • 在函式表示式儲存在變數後,變數也可作為一個函式使用:
  • var x = function (a, b) {return a * b};
  • var z = x(4, 3)
  • 函式其實在js中是物件,既然是物件也就有建構函式,那麼建立函式的方式又多了一種如下:
  • var myFunction = new Function("a", "b", "return a * b");
  • var x = myFunction(4, 3);

函式的提升

  • 在js中可以先使用後定義,其實在java中就是不可以的,在js中為什麼可以呢?原因是提升(Hoisting)是 JavaScript 預設將當前作用域提升到前面去的的行為
myFunction(5);

function myFunction(y) {
    return y * y;
}
  • 對於函式表示式也是一樣的。可以先呼叫後定義如下:
var z = x(4, 3);
var x = function (a, b) {return a * b};
  • 函式宣告與函式表示式
    • 其實函式宣告和函式表示式是有區別的。實際上解析器在向執行環境中載入資料時,對函式宣告和函式表示式並非一視同仁。解析器會率先讀取函式宣告,並使其在執行任何程式碼之前可用,至於函式表示式,則必須等到解析器執行到它所在的程式碼行,才會真正被解釋執行
      。請看下面的例子:
alert(sum(10,10));

function sum(num1,num2){
	return num1+num2;
}

函式是物件

在 JavaScript 中使用 typeof 操作符判斷函式型別將返回 “function” 。
但是JavaScript 函式描述為一個物件更加準確。

JavaScript 函式有屬性和方法。

  • 每個函式都有包含倆個屬性:lenght和prototypoe。其中,lenght屬性表示函式希望接收的命名引數的個數,如下:
function myFunction(a, b) {
    return arguments.length;
}

函式作為值來用

  • 如下:
function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3);
function myFunction(a, b) {
    return a * b;
}

var x = myFunction(4, 3) * 2;

js沒有過載(深入理解)

  • 將函式名想象為指標,也有助於理解為什麼ECMAScript中沒有函式蟲災的概念。
function addSomeNumber(num){
		return num+100;
}
function addSomeNumber(num){
		return num+200;
}
var result=addSomeNumber(100);//300

顯然,這個例子中聲明瞭倆個同名函式,而結果則是後面的函式覆蓋了前面的函式。以上程式碼實際上與下面的程式碼沒有什麼區別。

函式內部屬性

  • 在函式內部,有倆個特殊的物件:arguments 和this 。其中,argumengs它是一個類陣列物件,包含著出入函式中的所有引數。雖然arguments 的主要用途是儲存函式引數,但這個物件還有一個名叫callee的屬性,該屬性是一個指標,指向擁有這個arguments物件的函式。
		function factorial(num){
			if(num<=1){
				return 1;
			}else{
				return num*factorial(num-1);
			}
		}
  • 定義階乘函式一般都要用到遞迴演算法,如上面的程式碼所示,在函式有名字,而且名字以後也不會變的情況下,這樣定義沒有問題。但問題是這個函式的執行與函式名factorial緊緊耦合在一起了。為了消除這中緊密耦合的現象,可以像下面這樣使用 argumens.callee。
function factorial(num){
		if(num<=1){
			return 1;
		}else{
			return num*arguments.callee(num-1);
		}
}
  • 在這個重寫後的factorial()函式的函式體內,沒有在引用函式名factorial。這樣,無論引用函式時用的是什麼名字,都可以保證正常完成遞迴呼叫。例如:
var trueFactorial=factorial;
factorial =function(){
	return 0;
};
alert(trueFactorial(5));//120
alert(factorial(5)); //0
  • 在此,變數trueFactorial獲得了factorial的值,實際上是在另一個位置上儲存了一個函式的指標。然後我們又將一個簡單地返回0的函式賦值給factorial變數。如果像原來的factorial()那樣不使用arguments.callee,呼叫trueFactorial()就會返回0.可是在解除了函式體內的程式碼與函式名的耦合狀態後,trueFactorial()仍然能夠正常地計算階乘;至於factorila(),它現在只是一個返回0 的函式。
  • 每個函式都包含倆個非繼承而來的方法:apply()和call().這來個方法的用途都是在特定的作用域中呼叫函式,實際上等於設定函式體內this物件的值。首先,apply()方法接收倆個引數:一個是在其中執行函式的作用域,另一個是引數陣列。其中,第二個引數可以是Array的例項,也可以是arguments物件。例如:
function sum(num1,num2){
		return num1+num2;
}
function callSum1(num1,num2){
	return sum.apply(this,arguemnts);//傳入arguments引數
}
function callSum2(num1,num2){
	return sum.apply(this,[num1,num2]);//傳入陣列
}

alert(callSum1(10,10));//20
alert(callSum2(10,10));//20
  • 在上面這個例子中,callSum1()在執行sum()函式時傳入了this 作為this值(因為是在全域性作用域中呼叫的,所以傳入的物件就是window物件)和arguments物件。而calSum2同樣也呼叫了sum()函式,但是它傳入的則是this和一個引數陣列。這來個函式都會正常執行並返回正確的結果。
  • 在嚴格模式下,為指定環境物件而呼叫函式,則this值不會轉型為window。除非明確把函式新增到某個物件或者呼叫apply()或者call(),否則this值將是undefined。
  • call()方法與apply()方法的作用相同,它們的區別僅在於即接收引數的方式不同。對於call()方法而言,第一個引數是this值沒有變化,變化的是其餘引數都直接傳遞給函式。換句話說,在使用call方法時,傳遞給函式的引數必須逐個列舉出來,如下:
function sum(num1,num2){
	return num1+num2;
}
function callSum(num1,num2){
	return sum.call(this,num1,num2);
}

alert(callSum(10,10));//20
  • 在使用call方法的情況下,callSum()必須明確地傳入每一個引數。結果與使用apply()沒有什麼不同。至於是使用apply()還是call(),完全取決於你才用哪種給函式傳遞引數的方式最方便。如果你打算直接傳入arguments物件,或者包含函式中先接收到的也是一個數組,那麼使用apply()肯定更方便;否則,選擇call()可能更合適。
  • 事實上,傳遞引數並非apply()和call()真正的用武之地;它們真正強大的地方是能夠擴充函式賴以執行的作用域。來看一個例子:
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

  • 這個例子是在前面說明this物件的示例基礎上修改的。這一次,sayColor()也是作為全域性函式定義的,而且當在全域性作用域中呼叫它時,它確實會顯示“red" 因為對this.color的求值會轉換成對window.color的求值。而sayColor.call(this)和sayColor.call(window),則是倆個新鮮事地在全域性作用域中呼叫函式的方式,結果當前都會顯示“red".但是,當執行sayColor.call(o);時,函式的執行環境就不一樣了,因為此時函式體內的this指向了o,於是結果顯示的是“blue".
  • 使用call()或者apply() 來擴充作用域的最大好處,就是物件不需要與方法有任何耦合關係。在前面例子的第一個版本中,我們是先將sayColor()函式放到了物件o中,然後再通過o來呼叫它的;而在這裡重寫的例子中,就不需要先前那個多餘的步驟了。
  • ECMAScript5還定義了一個方法:bind()。這個方法會建立一個函式的例項,其this值會被繫結到傳給bind()函式的值。例如:
window.color ="red";
var o={color: "blue"};
function sayColor(){
	alert(this.color);
}
var objectSayColor=sayColor.bind(o);
objectSayColor();//blue
  • 在這裡,sayColor呼叫bind()並傳入物件o,建立了objectSayColor()函式。objectSayColor()函式的this值等於o,因此即使是在全域性作用域中呼叫這個函式,也會看到“blue"。

備註:參考了js紅寶書