第七章 函數表達式

定義函數的兩種方式:函數聲明函數表達式

    "use strct";
        // 函數聲明,指定函數名
        function functionName(arg0,arg1,arg2){
            // 函數體
        }
        // 函數聲明提升,即執行代碼之前會先讀取函數聲明
        sayHi(); //再調用
        function sayHi(){ //先讀聲明
            alert("hello world");  // hello worold
        }

匿名函數

       "use strct";
       // 函數表達式
       // 匿名函數,即創建一個函數並將它賦值給變量functionName
       // 沒有函數提升
       var functionName = function(arg0,arg1,arg2){
        //函數體
       };

遞歸

遞歸函數是一個在函數通過名字調用自身的情況下構成。

        //經典的遞歸階乘函數
        function factorial(num){
            if(num<=1){
                return 1;
            }else{
                return num*factorial(num-1);
            }
        }
        var anotherFactorial = factorial;
        // 存在弊端
        //如果factorial=null;anotherFactorial //error
        alert(anotherFactorial(3));

        // argument.callee 指向正在執行的函數指針
        "use strict"; // 存在弊端 嚴格模式無法運行
        function factorial(num){
            if(num<=1){
                return 1;
            }else{
                return num*arguments.callee(num-1);
            }
        }
        var anotherFactorial=factorial; 
        factorial=null;

        //命名函數表達式,在嚴格模式也能正常運行
        "use strict";
        var factorial=(function f(num){
            if(num<=1){
                return 1;
            }else{
                return num*f(num-1);
            }
        })
        var anotherFactorial= factorial;
        factorial=null;
        alert(anotherFactorial(3));

閉包

閉包是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。

        function createComparisonFunction(propertyName){
            return function(obj1,obj2){
                var val1=obj1[propertyName];
                var val2=obj2[propertyName];
                if(val1<val2){
                    return -1
                }else if(val1>val2){
                    return 1;
                }else{
                    return 0;
                }
            };
        }

createComparisonFunction() 函數執行完畢之後,其活動對象不會被銷燬,因爲匿名函數的作用域鏈仍然在引用這個活動對象。
即createComparisonFunction() 函數返回後,其執行環境環境的作用域鏈會被銷燬,但它活動對象仍然會留在內存中;知道匿名函數被銷燬後。
銷燬匿名函數 compareName=null

作用域鏈查找對象

            if(value1<value2){
                return -1;
            }else if(value1>value2){
                return 1;
            }else{
                return 0;
            }
        }
        var result = compare(5,10);

這裏寫圖片描述

閉包與變量

閉包只能取得包含函數中任何變量的最後一個值。閉包所保存的是整個變量對象,而不是某個特殊變量

        //每個函數的作用域鏈中都保存着createFunction() 函數的活動對象,引用都是同一變量i
        function createFunction(){
            var result = new Array();
            for(var i=0;i<10;i++){
                result[i] = function(){
                    return i;
                };
            }
            return result;
        }
    //創建另一個匿名函數強制讓閉包的行爲符合預期
    //重寫createFunction() 函數後,每個函數就會返回不同的索引值
    //沒有直接把閉包賦值給數組,而是定義了一個匿名函數,並將立即執行該匿名函數的結果賦給數組
        function createFunction(){
            var result = new Array();
            for(var i=0;i<10;i++){
                result[i]=function(num){
                    return function(){
                        return num;
                    }
                }(i);  
        }
        return result;
    }

關於this 對象

在閉包中使用this 對象可能會導致一些問題。this 對象是運行時基於函數執行環境綁定的:在全局函數中,this 等於window,而當函數被作爲某個對象方法調用時,this 等於那個對象。
匿名函數的執行環境具有全局性,因此this 通常指向window(通過call() 或apply() 改變函數的環境下,this會指向其他對象)

        var num=15;
        function num1(){
            var num=2;
            return function(){
                return this.num;
            }
        }
        alert(num1()());
            //把外部作用域中的this 對象保存在一個閉包能夠訪問的變量裏,就可以讓閉包訪問該對象
    var name = "The Window";
    var object={
        name:"My Object",
        getNameFunc : function(){
            var that =this;
            return function(){
                return that.name;
            };
        }
    };
    alert(object.getNameFunc()()); //My Object

內存泄漏

        function assignHandler(){
            var element=document.getElementById("someElement");
            var id=element.id;
            element.onclick=function(){
                alert(id);
            };
            element=null;
        }

模仿塊級作用域

    <script>
        //JavaScript 沒有塊級作用域。
        //意味着在塊級語句中定義的變量,實際上是包含函數中而非語句中創建的。
        //在Java、C++語句中,變量i只會在for 循環的語句塊中有定義,循環一旦結束,變量i就會被銷燬
        "use strict";
        function outputNumbers(count){
            for(var i=0;i<count;i++){
                document.write(i);
            }
            document.write(i);
        }
        outputNumbers(5);
    </script>
            // JavaScript 不會告訴是否多次聲明同一個變量;
            // 遇到這種情況,它只會對後續的聲明視而不見
            var i;
            document.write(i);

匿名函數可以用來模仿塊級作用域並避免重新聲明變量這個問題。

    <script>
        "use strict";
        //塊級作用域(通常稱爲私有作用域)的匿名函數的語法如下
        //代碼定義並立即調用了一個匿名函數。將函數聲明包含在一對圓括號中,表示它實際上是一個函數表達式。而緊隨其後的另一對括號會立即調用這個函數。
        (function(){
            //這裏是塊級作用域
        })();
    </script>

理解步驟:

    <script>
        "use strict";
        var count =5;
        outputNumbers(count);
        //把值直接傳遞給函數
        outputNumbers(5); // 變量只不過是值的另一種表現形式,因此可以用實際值替換
        //定義一個匿名函數,並把匿名函數賦值給變量someFunction。
        //調用函數的方式是在函數名稱後面添加一對圓括號,即someFunction()
        var someFunction=function(){
            // 這裏是塊級作用域
        };
        someFunction();
        //通過上面例子,可以使用實際值來替代變量count
        //那是否可以用函數值直接取代函數名?
        function(){
            //這裏是塊級作用域
        }(); //出錯
        //上面代碼會導致語法錯誤,因爲JavaScript 將function 關鍵字當作一個函數聲明的開始
        //函數聲明後面不能跟圓括號,而函數表達式可以跟圓括號
        //將函數聲明轉換成函數表達式,只需要象下面給它加一對圓括號即可
        (function(){
            //這裏是塊級作用域
        })();
    </script>

    <script>
        //無論什麼地方,只要臨時需要一些變量,就可以使用私有作用域
        //匿名函數中定義的任何變量,都會在執行結束時被銷燬
        function outputNumbers(count){
            (function () {
                for(var i=0;i<count;i++){

                    document.write(i);
                }
            })();
        }
        outputNumbers(5);
    </script>   

這種做法可以減少閉包占用內存的問題,因爲沒有指向匿名函數的引用。只要函數執行完畢,就可以立即銷燬其作用鏈。

私有變量

嚴格講,JavaScript 中沒有私有成員的概念;所有對象屬性都是公有的。但有一個私有變量概念,任何在函數中定義的變量,都可以認爲是私有變量,因爲不能在函數的外部訪問這些變量

    <script>
        //私有變量包括函數參數、局部變量和在函數內部定義的其他函數
        function add(num1,num2){
            var sum = sum1+sum2;
            return sum;
        }
    </script>

訪問私有變量共有辦法:通過函數內部創建一個閉包,閉包可以通過作用域鏈訪問。
把有權訪問私有變量和私有函數的共有方式稱爲特權方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章