JavaScript基础笔记,五 函数表达式

函数表达式

一、闭包

概念:闭包是指有权访问另一个函数作用域中变量的函数。

        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

如果一个闭包能访问的函数被执行完毕,作用域链(本质上是一个指向变量对象的指针列表)被销毁后,

其活动对象也不会被销毁,因为它们被闭包引用,知道闭包函数被销毁后,它们才被销毁。

由于闭包会携带包含它函数的作用域,所以会占用更多内存,建议能不使用闭包就不要使用闭包。

作用域链的这种配置机制引出了另外一个重要问题:闭包只能取得包含函数中任何一个变量的最后一个值。

        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变量的值,定义时的值不能说明问题。

解决办法:

        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的问题:

        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

内存泄漏问题:

        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;
            }
        }

二、模仿块级作用域

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.
        }

这种技术经常被用来限制在全局作用域中添加过多的变量和函数。

三、私有变量

私有变量:函数的参数、局部变量和在函数内部定义的函数。

共有方法 == 特权方法

        //在构造函数定义特权方法
        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