JavaScript基礎筆記(五) 函數表達式

函數表達式

1、閉包

概念:閉包是指有權訪問另外一個函數做用域中變量的函數。html

        function createCompareFun(propertyName) {
            return function (obj1, obj2) {
                var a = obj1[propertyName];
                var b = obj2[propertyName];

                if (a < b) {
                    return -1;
                }
                if (a > b) {
                    return 1;
                } else {
                    return 0;
                }
            }
        }

        //建立匿名函數
        var compare = createCompareFun("age");
        //調用該函數
        var result = compare({age:3},{age:8});
        console.log(result); //-1

若是一個閉包能訪問的函數被執行完畢,做用域鏈(本質上是一個指向變量對象的指針列表)被銷燬後,閉包

其活動對象也不會被銷燬,由於它們被閉包引用,知道閉包函數被銷燬後,它們才被銷燬。函數

因爲閉包會攜帶包含它函數的做用域,因此會佔用更多內存,建議能不使用閉包就不要使用閉包。this

做用域鏈的這種配置機制引出了另一個重要問題:閉包只能取得包含函數中任何一個變量的最後一個值。spa

        function createFun() {
            var result = new Array();
            for (var i = 0; i < 10; i++) {
                result[i] = function () {
                    return i;
                }
            }
            return result;
        };

        function getResult(fun, array) {
            for (var j = 0; j < fun.length; j++) {
                array[j] = fun[j]();
            }
            return array;
        }

        var fun = createFun();
        var array = new Array();
        var a = getResult(fun,array);
        console.log(a);  //[ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 ]

注意該問題的本質:注意函數實際執行時i變量的值,定義時的值不能說明問題。prototype

解決辦法:指針

        function createFun() {
            var result = new Array();
            for (var i = 0; i < 10; i++) {
                result[i] = function (num) {
                    return function () {
                        return num;
                    };
                }(i);
            }
            return result;
        };


        /*var b = function rightNow(a) {
            return a;
        }(5);
        console.log(b);*/

閉包中this的問題:code

        var name = "Window";
        var obj = {
            name: "Object",
            getFun: function () {
                return function () {
                    return this.name;
                };
            }
        };
        console.log(obj.getFun()()); //Window
        
        //解決:
        var obj2 = {
            name: "Obj2",
            getFun: function () {
                console.log("Fun's name: "+this.name);
                var v = this.name;
                return function () {
                    return v;
                }
            }
        }
        console.log(obj2.getFun()());
        var name = "Window";
        var obj = {
            name: "Object",
            getFun: function () {
                return this.name;
            }
        };

        var a = obj.getFun;
        console.log(a()); //Window
        console.log(obj.getFun()); //Object

內存泄漏問題:htm

        window.onload = function () {
            function outer() {
                var element = document.getElementById("testId");
                element.onclick = function () {
                    //閉包的做用域鏈中保存了一個html元素
                    //只要匿名函數存在,element的引用數最少是1,所以它所佔用的內存永遠不會被回收。
                    console.log(element.id);
                };
            }
            outer();

            //解決
            function outer2() {
                var element = document.getElementById("testId");
                var id = element.id;
                element.onclick = function () {
                    console.log(id);
                };
                //閉包會引用包含函數的整個活動對象,閉包即便不直接引用element,
                //包含函數的活動對象任然會保存一個引用,因此這步必須有
                element = null;
            }
        }

2、模仿塊級做用域

ECMAScript很操蛋地沒有塊級做用域:對象

        function Afun() {
            for (var i = 0; i < 10; i++){
                console.log(i);
            }
            //在外面從新聲明i,並無什麼卵用
            var i;
            console.log("Outer i is "+i);
        }
        Afun(); //Outer i is 10
        function Afun() {
            for (var i = 0; i < 10; i++){
                console.log(i);
            }
            //
            var i = 100;
            console.log("Outer i is "+i);
        }
        Afun(); //Outer i is 100

能夠用匿名函數來模仿塊級做用域:

        function Afun() {
            //因爲函數聲明後不能直接跟員括號來執行它
            //因此咱們用()包裹函數聲明把它轉換一個指向函數的引用
            (function () {
                for (var i = 0; i < 10; i++){
                    console.log(i);
                }
            })();
            console.log(i); //致使Error:i is not defined.
        }

這種技術常常被用來限制在全局做用域中添加過多的變量和函數。

3、私有變量

私有變量:函數的參數、局部變量和在函數內部定義的函數。

共有方法 ==  特權方法

        //在構造函數定義特權方法
        function MyObject() {
            //私有變量
            var v = 10;
            //私有函數
            function f() {
                return false;
            }

            //特權方法
            this.pm = function () {
                ++v;
                return v;
            }
        }

        var o = new MyObject();
        //除了pm()沒有任何途徑能訪問到真正的o.v
        console.log(o.v);  //undefined
        o.v = 166;
        //假的
        console.log(o.v);  //166
        console.log(o.pm());  //11

一)靜態私有變量

也能夠經過在私有做用域中定義私有變量和函數,建立特權方法。

        (function () {
            var v = 10;
            function f() {
                return false;
            }

            //注意這裏沒有var,是全局的
            //爲了保證全局,也沒有使用聲明函數
            MyObject = function () {
            };

            MyObject.prototype.pm = function () {
                v++;
                return f();
            };
        })();

        console.log(MyObject);
        console.log(new MyObject().pm()); //false
相關文章
相關標籤/搜索