學習JavaScript函數

JavaScript中的函數

JavaScript中的函數使用function關鍵字,後跟一組參數以及函數體。例如:函數

function foo(arg0, arg1,...,arg) {
    ...
}

var foo = function () {
  ...
};

(function () {
  ...
})();
複製代碼

JavaScript中有三種函數類型:函數聲明,函數表達式和函數構造器建立的函數。下面分別介紹這三種函數類型。工具

函數聲明

函數聲明由一系列的function關鍵字組成,依次爲:post

  • 有一個特定的名稱
  • 函數參數列表,包圍在括號中並由逗號分隔
  • 組成函數體的聲明語句

例如:學習

function foo(arg0, arg1,...,arg) {
  var a = 1;
  console.log(a);
}
複製代碼

函數表達式

函數表達式和函數聲明很是類似,依次爲:ui

  • 可選的名稱(當省略函數名的時候,該函數就成爲了匿名函數。)
  • 函數參數列表,包圍在括號中並由逗號分隔
  • 組成函數體的聲明語句

例如:spa

var foo = function () {
  var b = 2;
  console.log(b);
};

var foo = function bar() {
  var b = 2;
  console.log(b);
};

(function() {
    var c = 3;
    console.log(c);
})();
複製代碼

Function構造函數

能夠經過Function對象使用new操做符建立一個構造函數。例如:debug

new Function('alert('hello'); alert('world');');
複製代碼

函數聲明和函數表達式

這裏重點學習函數聲明和函數表達式。建立函數的最經常使用的兩個方法是函數表達式和函數聲明。調試

函數聲明:code

function 函數名稱 (參數:可選){ 函數體 }cdn

函數表達式:

function 函數名稱(可選)(參數:可選){ 函數體 }

因此,建立一個函數若是不聲明函數名稱,它確定是表達式,可若是聲明瞭函數名稱的話,如何判斷是函數聲明仍是函數表達式呢?

區分函數聲明和表達式最簡單的方法是看 function 關鍵字出如今聲明中的位 置(不單單是一行代碼,而是整個聲明中的位置)。若是 function 是聲明中 的第一個詞,那麼就是一個函數聲明,不然就是一個函數表達式。或者這麼理解function foo(){}是做爲賦值表達式的一部分的話,那它就是一個函數表達式,若是function foo(){}被包含在一個函數體內,或者位於程序的最頂部的話,那它就是一個函數聲明。

例如:

function foo(){} // 聲明,由於它是程序的一部分

var bar = function foo(){}; // 表達式,由於它是賦值表達式的一部分

new function bar(){}; // 表達式,由於它是new表達式

(function(){
    function bar(){} // 聲明,由於它是函數體的一部分
})();

(function foo(){}); // 表達式 包含在分組操做符內
複製代碼

匿名和具名函數

對於函數表達式,最熟悉的場景就是回調,例如:

setTimeout(function(){
    console.log("hello world!");
},1000);
複製代碼

這是一個匿名函數表達式,在平常開發和工具庫中這種模式很常見。可是它也有幾個缺點須要考慮。

  1. 匿名函數在棧追蹤中不會顯示出有意義的函數名,使得調試很困難。
  2. 若是沒有函數名,當函數須要引用自身時只能使用已通過期的arguments.callee引用, 好比在遞歸中。另外一個函數須要引用自身的例子,是在事件觸發後事件監聽器須要解綁 自身。
  3. 匿名函數省略了對於代碼可讀性/可理解性很重要的函數名。一個描述性的名稱可讓 代碼不言自明。

例如:

具名函數

function foo(){
    return bar();
}
function bar(){
    return baz();
}
function baz(){
    debugger;
}
foo();  //當調試器debugger的調用棧,棧中顯示了baz,bar,foo。
  
  
// 這是一個自執行的函數,函數內部執行自身,遞歸
function self() { 
    self(); 
}
複製代碼

匿名函數

function foo(){
    return bar();
  }
var bar = (function(){
    return function(){
      return baz();
    };
})();
function baz(){
    debugger;
}
foo();//當調試器debugger的調用棧,棧中顯示了baz,(anonymous),foo。


// 這是一個自執行的匿名函數,由於沒有標示名稱
// 必須使用arguments.callee屬性來執行本身
var self = function () { 
    arguments.callee(); //在嚴格模式下,ES5禁止使用 arguments.callee()。
};
複製代碼

給函數表達式指定一個函數名,不會影響其功能,反而是一個比較好的實現方式,例如:

setTimeout(function handle(){
    console.log("hello world!");
},1000);
複製代碼

當即執行函數表達式

(function foo() { 
    var a = 1;
    console.log(1);
})();

(function () { /* code */ } ()); // 推薦使用這個

(function () { /* code */ })(); // 可是這個也是能夠用的
複製代碼

因爲函數被包含在一對 ( ) 括號內部,所以成爲了一個表達式,經過在末尾加上另一個 ( ) 能夠當即執行這個函數,好比(function foo(){ .. })()。第一個 ( ) 將函數變成表 達式,第二個 ( ) 執行了這個函數。這種模式也叫當即執行函數表達式(IIFE)。

除了( ) 括號外,還有不少其餘的方式也能讓一個函數變成函數表達式,例如:

//&&,異或,逗號等操做符
true && function () { /* code */ } ();
1, function () { /* code */ } ();
//一元操做符號
!function () { /* code */ } ();
~function () { /* code */ } ();
-function () { /* code */ } ();
+function () { /* code */ } ();
複製代碼

可是依然使用( ) 括號,括號內部原本指望的就是函數表達式,主要是爲了方便開發人員閱讀。

IIFE一些常見應用,例如:

  • 把它們看成函數調用並傳遞參數進去
var a = 1;
(function ( global ) {
    var a = 2;
    console.log( a ); // 2 
    console.log( global.a ); // 1
})( window );
console.log( a ); // 1

複製代碼
  • 模塊方法
var module = (function () {
    var i = 0;
    return {
        get: function () {
            return i;
        },
        set: function (val) {
            i = val;
        },
        add: function () {
            return ++i;
        }
    };
} ());
module.get(); // 0
module.set(1);
module.add(); // 1
module.i // undefined 由於i不是返回對象的屬性
複製代碼

結尾

這一節是對學習JavaScript做用域中的函數做用域的一個補充。這樣把瑣碎的知識串聯起來,更容易造成體系。

相關文章
相關標籤/搜索