JavaScript函數表達式

1、序面試

      定義函數的方式有兩種:一種是函數聲明,另外一種就是函數表達式;算法

      1.1 函數聲明閉包

function functionName(arg){
     //函數體
}

      關於函數聲明,它有一個重要特徵就是函數聲明提高,意思就是在執行代碼以前會先讀取函數聲明。這就意味着能夠把函數放在調用它的語句後面。以下所示:函數

helloworld(); //在代碼執行以前會先讀取函數聲明
function helloworld(){
   console.log("hello world");
}

     1.2 函數表達式學習

var functionName=function(arg){
      //函數體
}

      這種形式看起來好像是常規的變量賦值語句,即建立一個函數並將它賦值給變量functionName。這種狀況下建立的函數叫作匿名函數。由於function關鍵字後面沒有標識符。spa

函數表達式與其餘表達式同樣,在使用以前必須先賦值;以下面代碼就會致使錯誤;指針

helloworld(); //錯誤,還未賦值,函數不存在

var helloworld=function(){
    console.log("hello world");
}

       有了函數表達式,咱們就能夠動態給函數表達式賦值了;以下面代碼:code

var helloworld; //聲明
if(condition){ //條件
   helloworld=function(){ //賦值
       console.log("hello world"); 
   }
}
else{
    helloworld=function(){ //賦值
       console.log("你好,世界");
    }
}

2、遞歸函數對象

     遞歸函數是在一個函數經過名字調用自身的狀況下構成的(和C#等語言同樣,因此程序的核心思想是差很少,只是在語法上有些差別,學好一門語言的基礎,學習其餘就會輕鬆不少),舉個經典的遞歸面試題,一列數的規則以下 : 1 、 1 、 2 、 3 、 5 、 8 、 13 、 21 、 34…… 求第 30 位數是多少,  用遞歸算法實現,代碼以下所示:blog

        function foo(n) {
            if (n <= 0)
                return 0;
            else if (n > 0 && n <= 2)
                return 1;
            else
                return foo(n - 1) + foo(n - 2);
        }

雖然這個函數代表看來沒有什麼問題,但下面的代碼卻可能致使它出錯:

        var foo1 = foo;
        foo = null;
        console.log(foo1(34));

以上代碼先把foo()函數保存在變量foo1中,而後將foo變量設爲null,結果指向原始函數的引用只剩下一個。但在接下來調用foo1()時,因爲必須執行foo(),而foo已經爲null了,因此就會致使錯誤;在這種狀況下,使用arguments.callee能夠解決這個問題。arguments.callee是一個指向正在執行的函數的指針,所以能夠用它來實現對函數的遞歸調用

     function foo(n) {
            if (n <= 0)
                return 0;
            else if (n > 0 && n <= 2)
                return 1;
            else
                return arguments.callee(n - 1) + arguments.callee(n - 2);
        }

也可使用命名函數表達式來達成相同的結果。例如:

        var foo = (function f(n) {
            if (n <= 0)
                return 0;
            else if (n > 0 && n <= 2)
                return 1;
            else
                return f(n - 1) + f(n - 2);
        });

 3、閉包

      3.1 閉包是指有權訪問另外一個函數做用域中的變量的函數,建立閉包的常見方式,就是在一個函數內部建立另外一個函數。要理解閉包,首先必須理解JavaScript特殊變量的做用域。變量的做用域無非就是兩種,全局變量和局部變量;接下來寫幾個demo來直觀表達;

函數內部直接讀取全局變量:

        var n = 100; //定義一個全局變量
        function fn() {
            console.log(n); //函數內部直接讀取全局變量
        }

        fn();

函數外部不能直接讀取局部變量:

        function fn() {
            var n = 100;
        }

        console.log(n); //n is not defined

在這裏有個地方須要注意的是,在函數內部聲明變量的時候,必定要用var ,若是沒用,則會變成全局變量:

        function fn() {
             n = 100;
        }
        fn();
        console.log(n); //100

有時候咱們須要獲得函數內部聲明的變量,因此可使用上面提到建立閉包的經常使用方式,在函數內部建立另外一個函數:

        function fn() {
            n = 100;

            function fn1() {
                console.log(n);
            }

            fn1();
        }
        fn(); //100

在上面的代碼中,函數fn1就被包括在函數fn內部,這時fm內部的全部局部變量,對fn1都是可見的。可是反過來就不行,fn1內部的局部變量,對fn就是不可見的。這就是Javascript語言特有的"鏈式做用域"結構,子對象會一級一級地向上尋找全部父對象的變量。因此,父對象的全部變量,對子對象都是可見的,反之則不成立。

居然fn1能夠讀取fn內部變量,那麼只要把fn1做爲返回值,這要咱們就能夠在外部讀取fn的變量了

        function fn() {
            n = 100;

            function fn1() {
                console.log(n);
            }

            return fn1;
        }
        
       var result=fn();
       result(); //100

 

在這裏fn1就是閉包,閉包就是可以讀取其餘函數內部變量的函數。因爲在Javascript語言中,只有函數內部的子函數才能讀取局部變量,所以能夠把閉包簡單理解成"定義在一個函數內部的函數"。因此,在本質上,閉包就是將函數內部和函數外部鏈接起來的一座橋樑。

       3.2  閉包的用途

       它的最大用處有兩個,一個是前面提到的能夠讀取函數內部的變量,另外一個就是讓這些變量的值始終保持在內存中。以下面代碼所示:

        function fn() {
            n = 100;

            nadd = function () {
                n += 1;
            }

            function fn1() {
                console.log(n);
            }

            return fn1;
        }

        var result = fn();
        result(); //100
        nadd();
        result(); //101

注意:因爲閉包函數會攜帶包含它的函數的做用域,所以會比其餘函數佔用更多的內存,過分使用閉包可能會致使內存佔用過多,因此在退出函數以前,將不使用的局部變量所有刪除。

 4、塊級做用域

       塊級做用域(又稱爲私有做用域)的匿名函數的語法以下所示:

(function(){
     //塊級做用域
})();

不管在什麼地方,只要臨時須要一些變量,就可使用私有做用域,好比:

        (function () {
            var now = new Date();
            if (now.getMonth() == 0 && now.getDate() == 1) {
                alert("新年快樂");
            }
        })();

把上面這段代碼放到全局做用域中,若是到了1月1日就會彈出「新年快樂」的祝福;這種技術常常在全局做用域中被用在函數外部,從而限制向全局做用域中添加過多的變量和函數。通常來講,咱們都應該儘可能少向全局做用域中添加變量和函數。在一個由不少開發人員共同參與的大型應用程序中,過多的全局變量和函數很容易致使命名衝突。而經過建立私用做用域,每一個開發人員既可使用本身的變量,又沒必要擔憂搞亂全局做用域。

相關文章
相關標籤/搜索