javascript 函數使用說明_基礎知識

來源:互聯網
上載者:User

什麼是函數(Function)
function sum(a,b){ 
     return a+b; 
} 
其實通俗的說就是一個有名稱的程式碼片段,方便重用。
要注意的是:
1.Javascript 的函數文法,因為Javascript本身就是區分大小寫,所以function不能寫作Function或FUNCTION.
2.sum是函數的名稱,這個並不是必須的,等以後我們會說到。
3.return是返回,如果不寫的話,函數的返回是undefined.如果要返回多個值,可以返回個數組或者對象(以後再說)
函數的調用

下面我們講函數的基本調用。

var result = sum(1,2) 
函數的參數

不好意思的說,這個才是本篇文章的重點。
執行個體一,參數少於 實際定義參數的數目
var result = sum(1); 
結果result 為NaN,這就說明了,參數b的值為undefined,但並不會報錯,這就無形中製造了bug.
執行個體二,參數多於 實際定義參數的數目
sum(1,2,3,4,5) 
結果為3.發現多於的參數被忽略了。
執行個體三,沒有參數的函數
function args(){return arguments;} 
每個函數裡都有一個預設的數組那就是arguments .它就是每個函數預設的參數為[] 。如果我們調用函數如下
args(1,2,3,4,5,6); 
會發現arguments的值為[1,2,3,4,5,6]。這下容易了,我們可以改造下上面的sum方法

function sum(){ 
    var res= 0; 
    for(i=0;i<arguments.length;i++){ 
        res+=arguments[i]; 
    } 
    return res; 
} 

sum(1,2,3,4); 
結果為10,這個sum函數靈活多了吧。^_^

Functions are data

這一篇是函數裡比較重要的概念,那就是函數是一個資料。看個例子
function f(){return 1;} 
var f=function(){return 1;} 
這兩個函數定義都是相同的。
typeof f; 
f的值為"function",所以說Javascript 的 函數是個資料類型。它有比較兩個重要的特點
1.它包含了代碼
2.能被執行
看個例子
var sum = function(a,b){return a+b;} 
var add = sum; 
sum=undefined; 
typeof sum; 
typeof add; 
add(1,2); 
我們把函數sum做為一個值賦給了add,發現刪除了sum不影響add的調用。所以函數也就是個正常的賦值。
匿名函數(Anonymous Functions)
Javascript中,可以不用寫賦值的代碼,如
"abcd" 1 [1,2,3] 
這樣都不會報錯,這些資料叫做匿名的。同樣的函數做為資料也可以是匿名的
function(a){return a} 
匿名函數的作用有兩個
1.可以把匿名函數做為一個參數傳入到另一個函數中。
2.你可以理解運行這個匿名函數
下面就會詳細討論這兩個功能的作用了。

回呼函數(Callback Functions)

因為函數和其他資料一樣可以被賦值,刪除,拷貝等,所以也可以把函數作為參數傳入到另一個函數中。
執行個體一
function invoke_and_add(a,b){ 
  return a()+b(); 
} 
function one(){ 
  return 1; 
} 
function two(){ 
  return 2; 
} 
invoke_and_add(one ,two); 
結果為3;
再來看看匿名函數的用法。
執行個體二
invoke_and_add(function(){return 1;},function(){return 2;}),直接一個函數體傳進去,沒有函數名。
我們稱,invoke_and_add為回呼函數 
我們用匿名函數替代了 one,two兩個函數。
通過上面兩個執行個體,回呼函數的定義為:傳遞一個函數A到另一個函數B中,並且這個函數B執行函數A。我們就說函數A叫做回呼函數。 說白了,就是被人直接調用的函數,在一個函數執行另一個函數!
如果沒有名稱,就叫做匿名回呼函數

回呼函數的作用

主要有三個
1.當函數作為參數傳遞的時候,不用為這個函數定義一個名稱,這樣的好處是減少了全域變數。
2.節省了大量的代碼。
3.提高了程式的效能。

自調用函數(Self-invoking Functions)

自調用函數也是匿名函數的一種表現形式,這個函數在定義之後,直接調用。如下

( 

function(){ 
   alert('haha'); 
} 
)() 

看起來還挺怪異,不過確實很簡單。
自調用函數很方便使用,可以不用定義更多的全域變數。還有個好處,就是這個函數不能被執行兩遍。真是非常適合做初始化的工作。
許多著名的javascript庫特別多的源碼中使用了這個功能,例如本人喜歡的Jquery.

內建函式(Inner Functions)

把函數作為一個值來思考一下,既然一個值可以定義在函數中,把函數做為資料放在函數中也未嘗不可。如下:
function a(param){ 
function b(theinput){        
   return theinput *2; 
} 
return 'The result is '+b(param); 
} 
也可以這麼寫
var a = function(param){ 
     var b = function(theinput){ 
        return theinput*2; 
     }; 
     return 'The result is '+b(param); 
}; 
b函數是在a函數之中的 ,也就是意味著,在a函數的外部是無法訪問b函數的。所以也稱之為私人函數(private function)
a(2); 
a(8); 
b(2); 
發現b(2)是沒有定義的。也就確定了它確實是私人函數。
內建函式的是使用還是有很多好處的。
1.可以有更少的全域變數。過多的使用全域變數就有可能由於命名衝突而產生過多的bugs
2.私人性,可以設計更好的介面函數供外部存取。

傳回值為函數的函數(Functions that Return Functions)

在前幾篇文章已經介紹了函數要有傳回值,即使沒有寫return,函數也會返回一個undefine。
接下來看看傳回值為函數的情況
function a(){ 
alert('a'); 
return function(){ 
  alert('b'); 
}; 
} 
在這個例子中,a函數執行了alert('a'),以及它返回了另一個函數b。關於返回b的調用我們可以這樣來用。
var newFunc = a(); 
newFunc(); 
執行結果為 alert a和alert b
如果不想賦值調用這個函數也可以簡寫如下
a()(); 
函數的自身重寫

因為函數可以返回一個函數,那就意味著可以用一個新的函數替代一箇舊的函數,根據前一個例子來改進一下
a=a(); 
第一次運行函數a,就alert a,再次運行函數a,就alert b,這個功能對初始化非常有用。這個函數a重寫了自己,避免在以後調用初始化的功能(上個例子為alert a)。
當然我們還有更簡單的方法來重寫a函數那就是在a函數的內部重寫它,來看看代碼
function a(){ 
alert("a") 
a=function(){ 
   alert("b"); 
} 
} 
只有在初次調用a函數才會執行 alert a 在以後的調用中,都會執行alert b
下面結合前幾節看個綜合的例子
var c = function(){ 
   function a(){ 
    alert('a') 
   } 
   function b(){ 
    alert('b') 
   } 
   a(); 
   return b; 
}();//alert('a'); 
c();//alert('b'); 
這個例子有以下幾點需要注意
1. a函數,b函數是內建函式。
2. return b 返回的是個函數的引用。
3. 子調用函數重寫了函數c。

如果能明白這個例子,關於內建函式,子調用函數和傳回值為函數的函數就可以都理解了。

閉包(Closures)閉包屬於比較難的一部分,在學習閉包之前,先來學習以下Javascript的範圍(Scope)

範圍鏈(Scope Chain)

函數內部定義的變數,在函數外不能訪問,或者變數定義在程式碼片段中(如if,for中),在程式碼片段之外也不可訪問。
var a =1; 
function f(){ 
var b=1; 
return a; 
} 
f();//a=1 
b;//b 沒有定義 
a 是全域變數,而b定義在函數f之中。所以:
在f函數中,a和b都可以訪問。
在f函數之外,a可以訪問,而b不可以

再次看個例子
var a = 1; 
function f(){ 
var b = 1; 
function n(){ 
  var c =3; 
} 
} 
如果定義一個函數n在函數f中,函數n不但可以訪問自己範圍的c,而且還能訪問所謂的父範圍的b和a
這就是範圍鏈(Scope Chain)
詞法範圍(Lexical Scope)

在Javascript中,也有詞法範圍(Lexical Scope),這個意思是,函數在定義的時候就產生了它的範圍,而不是在調用的時候,看個例子就明白了。
function f1(){var a=1;f2();} 
function f2(){return a;} 
f1();//a沒有定義 
先看看函數f1,調用了函數f2,因為函數局部變數a也在f1中,可能期望函數f2也訪問a,但事實卻不是這樣。
因為這個時候f2函數已經定義完畢了,而它的範圍沒有a被找到。不管是函數f1還是函數f2,僅僅能訪問的是本身的局部變 量或者全域變數。

用閉包來破壞這個範圍鏈(Breaking the Chain with Closure)

讓我們舉例來說明閉包吧。
執行個體1

function f(){ 
var b="b"; 
return function(){ 
  return b; 
} 
} 
函數f包含了變數b,在全域範圍中不可能訪問b,結果為沒有定義(undefined)。
看看這個函數的傳回值為一個函數。這個新的函數可以訪問f範圍中的變數b。看如下代碼
var n = f(); 
n();//訪問b 
由於函數f本身就是全域的,而它返回的函數又是個新的全域函數,而它又可以訪問函數f的範圍。

執行個體2
這個例子和上個例子結果是一樣的,但是有一點不同,函數f並不返回一個函數,而是建立一個全域變數n,代碼如下
var n; 
function f(){ 
var b = "b"; 
n=function(){ 
  return b; 
} 
} 
所以可以直接n()來訪問函數f裡的變數b

通過上面兩個例子我們就可以說當一個函數指向了它的父範圍,其作用是指向局部變數,就可以稱之為閉包。
閉包其實就提供了一個借口,一個讓外部存取內部的變數的方法

當我們建立了個傳遞參數的函數f,這個參數就變成了函數f的局部變數了。我們可以建立一個f的內建函式來返回這個參數
function f(arg){ 
var n =function(){ 
   return args; 
} 
  arg++; 
return n; 
} 
var m= f(123); 
m();//124 

閉包在迴圈中的應用

再迴圈中很容易引起一些bug,雖然表面是正常的。
看如下的代碼
function f(){ 
    var a = []; 
    var i; 
    for(i=0;i<3;i++){ 
        a[i] = function(){ 
            alert(i); 
            return i; 
        } 
    } 
    return a; 
} 
var a= f(); 
a[0]();//3 
a[1]();//3 
a[2]();//3 
建立個迴圈,我們的目的是每次迴圈都建立一個函數,函數返回的是迴圈的序列值也就是i。我們看看以上代碼的運行結果
都是為3.而我們期望的結果是1,2,3。
到底是為什麼呢?我們建立了三個閉包,都指向了變數i。閉包並沒有記住變數i的值,它僅是變數i的引用。在迴圈結束後,i的值是3,所以結果都是3了。

來看看正確的寫法
function f() { 
  var a = []; 
  var i; 
  for(i = 0; i < 3; i++) { 
    a[i] = (function(x){ 
      return function(){ 
        alert(x); 
        return x; 
      } 
    })(i); 
  } 
  return a; 
} 
var a = f(); 
a[0]();//0 
a[1]();//1 
a[2]();//2 
我們又用了一個閉包,把變數i變成了局部變數x了,x變數每次都是不同的值了。如果沒有徹底理解自調用函數那就如下寫法就可以明白了
function f() { 
  function makeClosure(x) { 
    return function(){ 
      return x; 
    } 
  } 
  var a = []; 
  var i; 
  for(i = 0; i < 3; i++) { 
    a[i] = makeClosure(i);  //makeClosure,用來記憶i的值。
  } 
  return a; 
} 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.