天天看點

js函數小結js函數小結

js函數小結

  • js函數小結
      • js函數的定義
      • 函數的提升
      • 函數是對象
      • JavaScript 函數有屬性和方法。
      • 函數作為值來用
    • 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紅寶書

繼續閱讀