JavaScript 匿名函数与闭包

匿名函数

没有函数名的函数

  • 单独的匿名函数是无法运行和调用的
  • 可以把匿名函数赋值给变量
  • 通过表达式自我执行,语法:(匿名函数)();
  • 匿名函数传递参数,语法:(匿名函数)(参数);
    <script>
        // 普通函数
        function func1(){
            alert("我是普通函数");
        }
        // func1();

        // 匿名函数
        // function(){
        //     alert("我是匿名函数");
        // }

        // 将匿名函数赋值给变量
        // var myfunc = function (){
        //     alert("我是匿名函数2");
        // }
        // alert(myfunc);//将函数表达式输出
        // myfunc();//运行匿名函数

        // 匿名函数通过表达式自我执行
        (function(name, age){
            alert("匿名函数自我执行")
        })();

        // 匿名函数传递参数
        (function(name, age){
            alert(name + "今年" + age + "岁")
        })("OliGit", 23);
    </script>

闭包

概念

  • 闭包的英文单词是closure,是指有权访问另一个函数作用域中变量的函数
  • 在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。内层的函数可以使用外层函数的所有变量,即使外层函数执行完毕
  • 这是JavaScript中非常重要的一部分知识,因为使用闭包可以大大减少我们的代码量,使我们的代码看起来更加清晰等等,总之功能十分强大

相关知识点

  • 常见的方式是在函数内部创建另一个函数
   function myfunc(){
       return function(){
           return("***********");
       }
   }

   // alert(myfunc);//输出整个函数表达式
   // alert(myfunc());//输出匿名函数表达式
   
   // 调用闭包方式1
   // alert(myfunc()());//输出匿名函数返回值
   
   //调用闭包方式2
   var bb = myfunc();
   alert(bb());
  • 闭包的第一个用途:通过闭包可以访问局部变量
   // 通过闭包访问局部变量
   
   function func(){
       var jb = "局部变量";
   }
   // alert(jb);//函数外无法访问函数内的局部变量

   function func2(){
       var jb2 = "局部变量2";
       return function(){
           return(jb2);//通过匿名函数返回func2()的局部变量jb2
       }
   }
   // 调用方式1
   // alert(func2()());
   
   // 调用方式2
   var JB2 = func2();
   alert(JB2());
  • 闭包的第二个用途:可以让这些变量的值始终保持在内存中
    1、忧点:可以把局部变量驻留在内存中,可以避免使用全局变量;(全局变量在复杂程序中会造成许多麻烦(比如命名冲突,垃圾回收等),所以推荐使用私有的封装的局部变量。而闭包可以实现这一点)
    2、缺点:由于闭包里作用域返回的局部变量资源不会被立刻销毁回收,所以可能会占用更多的内存;所以过度使用闭包会导致性能下降
   var num = 100;//全局变量
   function add(){
       alert(++num);
   }
   // add();
   // add();
   // add();//每执行函数一次,num累加一次

   function add2(){
       var num2 = 100;//局部变量
       alert(++num2);
   }
   //每调用一次初始化局部变量一次,所以无法实现累加
   // add2();add2();add2();

   //通过闭包实现局部变量的累加
   function add3(){
       var num3 = 100;//局部变量

       return function(){
           alert(++num3);
       }
   }
   // 调用方式1:无法实现累加,因为每次调用num3都会初始化一次,
   // 这是因为每一次都是重新调用函数到结束,再重新调用一次,所以num3都会初始化一次
   // add3()();add3()();add()();//101

   // 调用方式2:可以实现累加,因为外部函数add3只执行一次,即num3只初始化一次
   //后面执行三次的都是匿名函数,而匿名函数没有初始化num3,所以可是实现累加
   var ADD3 = add3();//这里只初始化一次
   ADD3();ADD3();ADD3();//101,102,103

   ADD3 = null;//最后赋值为空,及时解除引用,否则会占用更多内存

闭包中的this

匿名函数的执行环境具有全局性,this通常是指向window的

  • 可以使用对象冒充强制改变this的指向
  • 将this赋值给一个变量,闭包访问这个变量
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>闭包中的this</title>
</head>
<body>
    <script>
        var name = "The Window";

        var obj1 = new Object();
        obj1.name = "The Object";
        obj1.getName = function(){
            return this.name;
        }
        // alert(obj1.getName());//返回The Object

        var obj2 = {
            name : "The Object2",
            getName: function(){
                return this.name;
            }
        }
        // 属性的函数内
        // alert(obj2.getName());//返回The Object2

        // 闭包里的this/
        var obj3 = {
            name : "The Object3",
            getName : function(){
                return function(){
                    // 匿名函数的执行环境具有全局性,this通常是指向window的
                    return this.name;
                }
            }
        }
        // alert(obj3.getName()());//返回The Window
        
        // 如果想访问属性name
        // 方法一:可以使用对象冒充call()强制改变this的指向
        // alert(obj3.getName().call(obj3));//返回The Window

        // 方法二:将this赋值给一个变量,闭包访问这个变量
        var obj4 = {
            name : "The Object4",
            getName : function(){
                //返回的值object对象,属性方法this指向object,也可以看最上面的2个例子
                // alert(this);
                // 所以可以在属性方法里将this赋值给一个变量,然后匿名函数访问这个变量即可
                var self = this;//this指向Object对象
                return function(){
                    // 闭包函数可以向上访问父函数的属性
                    return self.name;
                }
            }
        }
        alert(obj4.getName()());
    </script>
</body>
</html>

循环函数中的匿名函数和闭包

循环函数中的匿名函数

        function func1(){
            var arr = [];
            for (var i = 0; i < 5; i++){
                arr[i] = "元素" + i;
            }
            return(arr);
        }
        // alert(func1());//元素0,元素1,元素2,元素3,元素4

        //循环里包含匿名函数
        function func2(){
            var arr2 = [];
            for (var i = 0; i < 5; i++){
                arr2[i] = function(){
                    return "元素" + i;
                };//此时匿名函数没有执行,里面的i为参数
            }
            return(arr2);//此时arr2数组内5个空间都为匿名函数
        }
        // alert(func2());//输出一个包含五个匿名函数的数组,证明匿名函数没有执行
        //function(){ return "元素" + i; },
        //function(){ return "元素" + i; },
        //function(){ return "元素" + i; },
        //function(){ return "元素" + i; },
        //function(){ return "元素" + i; }

        var Bb = func2();//循环已经结束,此时的i已经变成5,当调用里面的
        // 匿名函数时,将i = 5 传入匿名函数内
        // alert(Bb[0]);   alert(Bb[1]); //function(){ return "元素" + i; }
        // alert(Bb[0]());     alert(Bb[1]()); //元素5, 元素5
        // for (var i = 0; i < 5; i++){
        //     alert(Bb[i]());//全是元素5,没有得到想要的结果
        // }

        // 让匿名函数立刻执行赋值
        function func3(){
            var arr3 = [];
            for (var i = 0; i < 5; i++){
                arr3[i] = (function(){
                    return "元素" + i;
                })()//匿名函数立刻执行
            }
            return arr3;//此时arr3数组内得到的为匿名函数的返回值
        }
        var Bb3 = func3();
        // alert(Bb3.length);//5
        // alert(Bb3);//元素0,元素1,元素2,元素3,元素4
        // alert(Bb3[0]);//元素0

循环函数中的闭包

        function func4(){
            var arr4 = [];
            for (var i = 0; i < 5; i++){
                arr4[i] = (function(n){
                    return function(){
                        return "元素" + n;
                    }
                })(i)
            }
            return arr4;
        }
        var Bb4 = func4();
        // alert(Bb4.length);//5
        for (var i = 0; i < 5; i++){
            // alert(Bb4[i]);
            alert(Bb4[i]());//成功输出元素0-4
        }

模仿块级作用域

块级作用域又叫私有作用域,单数JS没有块级作用域的概念;这意味着在块语句(比如for语句)中定义的变量,不会因为离开了for语句后就失效

  • 使用了块级作用域后,匿名函数中定义的任何变量,都会在执行结束时被销毁
  • 一般来说,我们都应该尽可能少向全局作用域中添加变量和函数;过多的全局变量和函数很容易导致命名冲突
  • 使用块级作用域,每个开发者既可以使用自己的变量,又不担心搞乱全局作用域
  • 在全局作用域中使用块级作用域可以减少闭包占用内存问题
function func(){
            for (var i = 0; i < 5; i++){
                // i不会因为离开了for语句就失效
            }
            var i;//即使重新声明一个i变量,结果还是5
            alert(i);//i输出5
        }
        // func();

        // 模仿块级作用域
        function func2(){
            (function(){
                for (var i = 0; i < 5; i++){

                }
            })()
            alert(i);//此时的i已经不存在,会报错:"i is not defined"
        }
        func2();

私有变量

JavaScript没有私有属性概念;所有的属性都是公用的
私有变量的概念:在任何函数中定义的变量,都是私有变量,因为不能在函数外部访问这些变量

  • 私有变量:包括函数的参数、局部变量和函数内部定义的其他函数
  • 特权方法:内部创建一个闭包,闭包可以访问私有变量;因此创建用于访问私有变量的公用方法,称作特权方法
  • 可以通过构造方法传参来访问私有变量(这种方法的缺点是会为每一个实例创建一组新的方法,不能实现共享)
    <script>
        function sum(){
            var m = 100;
        }
        // alert(m);//会报错,私有变量(局部变量),外部无法访问

        function people(){
            this.name = "OliGit";
            this.say = function(){
                return "我是" + this.name;
            }
        }
        // var pson = new people();
        // alert(pson.name);//OliGit
        // alert(pson.say());//我是OliGit

        function people2(){
            var name = "OliGit";//私有变量
            function say(){//私有方法、函数
                return "我是" + name;
            }
            this.getName = function(){
                return name;
            }
            this.getSay = function(){
                return say();
            }
        }
        var pson2 = new people2();
        // alert(pson2.name);//undefined
        // alert(pson2.age);//undefined
        // alert(pson2.say());//say is not a function

        alert(pson2.getName());//OliGit
        alert(pson2.getSay());//我是OliGit

		// 通过构造方法传参来访问私有变量
        function people3(_name){
            var name = _name;
            this.getName =  function(){
                return name;
            }
            this.setName = function(value){
                name = value;
            }

        }
        var pson3 = new people3("OliGit");
        alert(pson3.getName());
        
        pson3.setName("奥利给");
        alert(pson3.getName());
    </script>

静态私有变量

通过块级作用域(私有作用域)中定义私有变量或函数,创建对外公共的特权方法

  • 首先创建私有作用域
  • 定义私有变量或函数
  • 定义构造函数和特权方法
  • 这种方式创建的私有变量因为使用原型而实现共享
  • 同时由于共享,实例也就没有自己的私有变量
    <script>
        (function(){
            var name = "OliGit";
            User = function(){}// 构造函数
            User.prototype.getName = function(){
                return name;
            }
            User.prototype.setName = function(value){
                name = value;
            }
        })()
        var VIP1 = new User();
        // alert(VIP1.getName());
        VIP1.setName("Olivia");
        alert(VIP1.getName());//Olivia

        var VIP2 = new User();
        alert(VIP2.getName());//Olivia
    </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值