• JavaScript使用函数


    函数(function)就是一段被封装的代码,允许反复调用。不仅如此,在JavaScript中,函数可以作为表达式参与运算,可以作为闭包存储信息,也可以作为类型构造实例等。JavaScript拥有函数式编程的很多特性,灵活使用函数,可以编写出功能强大、代码简洁、设计优雅的程序。

    1、定义函数

    1.1、声明函数

    使用function关键字可以声明函数,具体语法格式如下:

        function funName([args]){
            statements
        }
    
    • 1
    • 2
    • 3

    funName表示函数名,必须是合法的标识符。在函数名之后是由小括号包含的参数列表,参数之间以逗号分隔,参数是可选项,没有数量限制。

    【示例】最简单的函数体是一个空函数,不包含任何代码:

        function funName(){}                       //空函数
    
    • 1

    提示:var和function都是声明语句,它们声明的变量和函数在JavaScript预编译期被解析,这种现象被称为变量提升或函数提升,因此在代码的底部声明变量或函数,在代码的顶部也能够访问。在预编译期,JavaScript引擎会为每个function创建上下文运行环境,定义变量对象,同时把函数内所有私有变量作为属性注册到变量对象上。

    1.2、构造函数

    使用Function()可以构造函数,具体语法格式如下:

        var funName = new Function(p1, p2, ... , pn, body);
    
    • 1

    Function()的参数类型都是字符串,p1~pn表示所创建函数的参数列表,body表示所创建函数的函数体代码,在函数体内语句之间通过分号进行分隔。

    【示例1】构造一个函数:求两个数的和。参数a和b用来接收用户输入的值,然后返回它们的和:

        var f = new Function("a", "b", "return a+b");        //通过构造函数创建函数结构
    
    • 1

    在上面代码中,f就是所创建函数的名称。如果使用function语句可以设计相同结构的函数:

        function f(a, b){                              //使用function语句定义函数结构
            return a + b;
        }
    
    • 1
    • 2
    • 3

    【示例2】使用Function()构造函数可以不指定任何参数,表示创建一个空函数:

        var f = new Function();                       //定义空函数
    
    • 1

    【示例3】在Function()构造函数参数中,p1~pn表示参数列表,可以分开传递,也可以合并为一个字符串进行传递。下面三行代码定义的参数都是等价的:

        var f = new Function("a", "b", "c", "return a+b+c")
        var f = new Function("a, b, c", "return a+b+c")
        var f = new Function("a,b", "c", "return a+b+c")
    
    • 1
    • 2
    • 3

    提示:使用Function()可以动态创建函数,这样可以把函数体作为一个字符串表达式进行设计,而不是作为一个程序结构,因此使用起来会更灵活。Function()的缺点如下:

    • Function()构造函数在执行期被编译,执行效率较低。
    • 函数体包含的所有语句,将以一行字符串的形式进行表示,代码的可读性较差。

    因此,Function()构造函数不是很常用,也不推荐使用。

    1.3、函数直接量

    函数直接量也称为函数表达式、匿名函数,没有函数名,仅包含function关键字、参数列表和函数体。具体语法格式如下:

        function([args]){
            statements
        }
    
    • 1
    • 2
    • 3

    【示例1】定义一个函数直接量:

        function(a, b){                         //函数直接量
            return a + b;
        }
    
    • 1
    • 2
    • 3

    在上面代码中,函数直接量与使用function语句定义函数结构基本相同,它们的结构都是固定的。但是函数直接量没有指定函数名。

    【示例2】匿名函数可以作为一个表达式使用,也称为函数表达式,而不再是函数结构块。下面把匿名函数作为一个表达式,赋值给变量f:

        var f = function(a, b){
            return a + b;
        };
    
    • 1
    • 2
    • 3

    当把函数作为一个表达式赋值给变量之后,变量就可以作为函数被调用:

        console.log(f(1,2));                       //返回数值3
    
    • 1

    【示例3】匿名函数可以直接参与表达式运算。下面把函数定义和调用合并在一起编写:

        console.log(                                //把函数作为一个操作数进行调用
            (function(a, b){
                return a + b;
            })(1,2));                               //返回数值3
    
    • 1
    • 2
    • 3
    • 4

    1.4、箭头函数

    ECMAScript 6新增箭头函数,它是一种特殊结构的函数表达式,语法比function函数表达式更简洁,并且没有自己的this、arguments、super或new.target,不能用作构造函数,不能与new一起使用。语法格式如下:

        (param1, param2,, paramN) => { statements }
        (param1, param2,, paramN) => expression
    
    • 1
    • 2

    其中,param1, param2, …, paramN表示参数列表,statements表示函数内的语句块,expression表示函数内仅包含一个表达式,它相当于如下语法:

        function (param1, param2,, paramN) { return expression; }
    
    • 1

    当只有一个参数时,小括号是可选的:

        (singleParam) => { statements }                //正确
        singleParam => { statements }                  //正确
    
    • 1
    • 2

    没有参数时,需要使用空的小括号表示:

        () => { statements }
    
    • 1

    【示例1】使用箭头函数定义一个求平方的函数:

        var fn = x => x * x;
    
    • 1

    等价于:

        var fn = function (x) {
            return x * x;
        }
    
    • 1
    • 2
    • 3

    【示例2】定义一个比较函数,比较两个参数,返回最大值:

        var fn = (x,y) => {
            if (x > y) {
                return x;
            } else {
                return y;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、调用函数

    调用函数有4种模式:常规调用、方法调用、动态调用、实例化调用。下面进行详细讲解。

    2.1、常规调用

    在默认状态下,函数是不会被执行的。使用小括号(())可以执行函数,在小括号中可以包含零个或多个参数,参数之间通过逗号进行分隔。

    【示例1】使用小括号调用函数,然后把返回值作为参数,再传递给f()函数,进行第二轮运算,这样可以节省两个临时变量:

        function f(x,y){                     //定义函数
            return x*y;                      //返回值
        }
        console.log(f(f(5,6),f(7,8)));       //返回1680。重复调用函数
    
    • 1
    • 2
    • 3
    • 4

    【示例2】如果函数返回值为一个函数,则在调用时可以使用多个小括号反复调用:

        function f(x, y){                  //定义函数
            return function(){             //返回函数类型的数据
                return x * y;
            }
        }
        console.log(f(7, 8)());            //返回值56,反复调用函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2、函数的返回值

    在函数体内,使用return语句可以设置函数的返回值,一旦执行return语句,将停止函数的运行,并运算和返回return后面的表达式的值。如果函数不包含return语句,则执行完函数体内所有语句后,返回undefined值。

    【示例1】函数的参数没有限制,但是返回值只能是一个,如果要输出多个值,可以通过数组或对象进行设计:

        function f(){
            var a = [];
            a[0] = true;
            a[1] = 123;
            return a;               //返回多个值
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在上面代码中,函数返回值为数组,该数组包含两个元素,从而实现使用一个return语句,返回多个值的目的。

    【示例2】在函数体内可以包含多个return语句,但是仅能执行一个return语句,因此在函数体内可以使用分支结构决定函数返回值:

        function f(x, y){
            //如果参数为非数字类型,则终止函数执行
            if( typeof x != "number" ||  typeof y != "number") return;
            //根据条件返回值
            if(x > y) return x - y;
            if(x < y) return y - x;
            if(x * y <= 0) return x + y;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.3、方法调用

    当一个函数被设置为对象的属性值时,称为方法。使用点语法可以调用一个方法。

    【示例】创建一个obj对象,它有一个value属性和一个increment方法。increment方法接收一个可选的参数,如果该参数不是数字,则默认使用数字1:

        var obj = {
            value : 0,
            increment : function(inc) {
                this.value += typeof inc === 'number' ? inc : 1;
            }
        }
        obj.increment();
        console.log(obj.value);                 //1
        obj.increment(2);
        console.log(obj.value);                 //3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用点语法调用对象obj的方法increment,然后通过increment()函数改写value属性的值。在increment方法中可以使用this访问obj对象,然后使用obj.value方式读写value属性值。

    2.4、动态调用

    call和apply是Function的原型方法,它们能够将特定函数当作一个方法绑定到指定对象上,并进行调用。具体语法格式如下:

        function.call(thisobj, args...)
        function.apply(thisobj, [args])
    
    • 1
    • 2

    function表示要调用的函数。参数thisobj表示绑定对象,也就是将函数function体内的this动态绑定到thisobj对象上。参数args表示将传递给被函数的参数。

    call只能接收多个参数列表,而apply只能接收一个数组或者伪类数组,数组元素将作为参数列表传递给被调用的函数。

    【示例1】使用call动态调用函数f,并传入参数值3和4,返回两个值的和:

        function f(x,y){                             //定义求和函数
            return x+y;
        }
        console.log( f.call(null, 3, 4));            //返回7
    
    • 1
    • 2
    • 3
    • 4

    在上面示例中,f是一个简单的求和函数,通过call方法把函数f绑定到空对象null身上,以实现动态调用函数f,同时把参数3和4传递给函数f,返回值为7。实际上,f.call(null, 3, 4)等价于null.m(3,4)。

    【示例2】示例1使用call调用,也可以使用apply方法调用函数f:

        function f(x,y){                              //定义求和函数
            return x+y;
        }
        console.log( f.apply(null, [3, 4] ));         //返回7
    
    • 1
    • 2
    • 3
    • 4

    如果把一个数组或伪类数组的所有元素作为参数进行传递,使用apply方法就非常方便。

    【示例3】使用apply方法设计一个求最大值的函数:

        function max(){                                        //求最大值函数
            var m = Number.NEGATIVE_INFINITY;                  //声明一个负无穷大的数值
            for( var i = 0; i < arguments.length; i ++ ){      //遍历所有实参
                if( arguments[i] > m )                         //如果实参值大于变量m,
                m = arguments[i];                              //则把该实参值赋值给m
            }
            return m;                                          //返回最大值
        }
        var a = [23, 45, 2, 46, 62, 45, 56, 63];               //声明并初始化数组
        var m = max.apply( Object, a );                        //动态调用max,绑定为Object的方法
        console.log( m );                                      //返回63
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在上面示例中,设计定义一个函数max(),用来计算所有参数中最大值参数。首先,通过apply方法,动态调用max()函数。其次,把它绑定为Object对象的一个方法,并把包含多个值的数组传递给它。最后,返回经过max()计算后的最大数组元素。在上面示例中,设计定义一个函数max(),用来计算所有参数中最大值参数。首先,通过apply方法,动态调用max()函数。其次,把它绑定为Object对象的一个方法,并把包含多个值的数组传递给它。最后,返回经过max()计算后的最大数组元素。

    如果使用call方法,就需要把数组内所有元素全部读取出来,再逐一传递给call方法,显然这种做法不是很方便。

    【示例4】可以动态调用Math的max()方法计算数组的最大值元素:

        var a = [23, 45, 2, 46, 62, 45, 56, 63];            //声明并初始化数组
        var m = Math.max.apply( Object, a );                //调用系统函数max
        console.log( m );                                   //返回63
    
    • 1
    • 2
    • 3

    2.5、实例化调用

    使用new命令可以实例化对象,在创建对象的过程中会运行函数。因此,使用new命令可以间接调用函数。

    注意:使用new命令调用函数时,返回的是对象,而不是return的返回值。如果不需要返回值,或者return的返回值是对象,可以选用new间接调用函数。

    【示例】使用new调用函数,把传入的参数值显示在控制台:

        function f(x,y){                           //定义函数
            console.log("x = " + x + ", y = " + y );
        }
        new f(3, 4);
    
    • 1
    • 2
    • 3
    • 4

    3、函数参数

    参数是函数对外联系的唯一入口,用户只能通过参数控制函数的运行。

    3.1、形参和实参

    函数的参数包括两种类型:

    • 形参:在定义函数时,声明的参数变量,仅在函数内部可见。
    • 实参:在调用函数时,实际传入的值。

    【示例1】定义JavaScript函数时,可以设置零个或多个参数:

        function f(a,b){                //设置形参a和b
            return a+b;
        }
        var x=1,y=2;                    //声明并初始化变量
        console.log(f(x,y));            //调用函数并传递实参
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在上面示例中,a、b就是形参,而在调用函数时向函数传递的变量x、y就是实参。

    一般情况下,函数的形参和实参数量应该相同,但是JavaScript并没有要求形参和实参必须相同。在特殊情况下,函数的形参和实参数量可以不相同。

    【示例2】如果函数实参数量少于形参数量,那么多出来的形参的值默认为undefined:

        (function(a,b){                       //定义函数,包含两个形参
            console.log(typeof a);            //返回number
            console.log(typeof b);            //返回undefined
        })(1);                                //调用函数,传递一个实参
    
    • 1
    • 2
    • 3
    • 4

    【示例3】如果函数实参数量多于形参数量,那么多出来的实参就不能够通过形参进行访问,函数会忽略多余的实参。下面函数的实参3和4就被忽略了:

        (function(a,b){                     //定义函数,包含两个形参
            console.log(a);                 //返回1
            console.log(b);                 //返回2
        })(1,2,3,4);                        //调用函数,传入4个实参值
    
    
    • 1
    • 2
    • 3
    • 4
    • 5

    提示:ECMAScript 6开始支持默认参数,以前设置默认参数的方法如下。

        function add(a , b) {
            b = b || 1;                    //判断b是否为空,为空就给默认值1
        }
    
    • 1
    • 2
    • 3

    现在可以设置:

        function add(a , b=1) {            //如果参数b为空,则使用默认值1
        }
    
    • 1
    • 2

    3.2、获取参数个数

    使用arguments对象的length属性可以获取函数的实参个数。arguments对象只能在函数体内可见,因此arguments.length只能在函数体内使用。

    使用函数对象的length属性可以获取函数的形参个数,该属性为只读属性,在函数体内、体外都可以使用。

    3.3、使用arguments

    arguments对象表示函数的实参集合,仅能够在函数体内可见,并可以直接访问。

    【示例1】函数没有定义形参,但是在函数体内通过arguments对象可以获取调用函数时传入的每一个实参值:

        function f(){                                        //定义没有形参的函数
            for(var i = 0; i < arguments.length; i ++ ){     //遍历arguments对象
                console.log(arguments[i]);                   //显示指定下标的实参的值
            }
        }
        f(3, 3, 6);                                          //逐个显示每个传递的实参
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    注意:arguments对象是一个伪类数组,不能够继承Array的原型方法。可以使用数组下标的形式访问每个实参,如arguments[0]表示第一个实参,下标值从0开始,直到arguments.length-1。其中,length是arguments对象的属性,表示函数包含的实参个数。同时,arguments对象可以允许更新其包含的实参值。

    【示例2】使用for循环遍历arguments对象,然后把循环变量的值传入arguments,以便改变实参值:

        function f(){
            for(var i = 0; i < arguments.length; i ++ ){    //遍历arguments对象
                arguments[i] =i;                            //修改每个实参的值
                console.log(arguments[i]);                  //提示修改的实参值
          }
        }
        f(3, 3, 6);                                         //返回提示0、1、2,而不是3、3、6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    【示例3】通过修改length属性值,可以改变函数的实参个数。当length属性值增大时,则增加的实参值为undefined;如果length属性值减小,则会丢弃length长度值之后的实参值:

        function f(){
            arguments.length = 2 ;                         //修改arguments对象的length属性值
            for(var i = 0; i < arguments.length; i ++ ){
                 console.log(arguments[i]);
            }
        }
        f(3, 3, 6);                                        //返回提示3、3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.4、使用callee

    callee是arguments对象的属性,它引用当前arguments对象所属的函数。使用该属性可以在函数体内调用函数自身。在匿名函数中,callee属性比较有用,例如,利用它可以设计递归调用。

    【示例】使用arguments.callee获取匿名函数,然后通过函数的length属性获取函数形参个数,最后比较实参个数与形参个数,以检测用户传递的参数是否符合要求:

        function f(x, y, z){
            var a = arguments.length;              //获取函数实参的个数
            var b = arguments.callee.length;       //获取函数形参的个数
            if (a != b){                           //如果形参和实参个数不相等,则提示错误信息
                throw new Error("传递的参数不匹配");
            }
            else{                                  //如果形参和实参数目相同,则返回它们的和
                return x + y + z;
            }
        }
        console.log(f(3, 4, 5));                   //返回值为12
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    arguments.callee等价于函数名,在上面示例中,arguments.callee等于f。

    3.5、剩余参数

    ECMAScript 6新增剩余参数,它允许将不定数量的参数表示为一个数组。语法格式如下:

        function(a, b, ...args) {
            //函数体
        }
    
    • 1
    • 2
    • 3

    如果函数最后一个形参以…为前缀,则它表示剩余参数,将传递的所有剩余的实参组成一个数组,传递给形参args。

    提示:剩余参数与arguments对象之间的区别主要有如下三点:

    • 剩余参数只包含没有对应形参的实参,而arguments对象包含所有的实参。
    • 剩余参数只包含没有对应形参的实参,而arguments对象包含所有的实参。
    • arguments对象有自己的属性,如callee等。

    【示例】利用剩余参数设计一个求和函数:

        var fn = (x, y, ...rest) => {
            var i, sum = x + y;
            for (i=0; i<rest.length; i++) {
                sum += rest[i];
            }
            return sum;
        }
        console.log( fn(5, 7, 6, 4, 7));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以简写为:

        var fn = (...rest) => {
            var i, sum = 0;
            for (i=0; i<rest.length; i++) {
                sum += rest[i];
            }
            return sum;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4、函数作用域

    JavaScript支持全局作用域和局部作用域,局部作用域也称为函数作用域,局部变量在函数体内可见,因此也称为私有变量。

    4.1、定义作用域

    作用域(scope)表示变量的作用范围、可见区域,一般包括词法作用域和执行作用域。

    • 词法作用域:根据代码的结构关系确定作用域。它是一种静态的词法结构,JavaScript解析器主要根据词法结构确定每个变量的可见范围和有效区域。
    • 执行作用域:当代码被执行时,才能够确定变量的作用范围和可见性,与词法作用域相对,它是一种动态作用域。函数的作用域会因为调用对象不同而发生变化。

    注意:JavaScript支持词法作用域,JavaScript函数只能运行在被预先定义好的词法作用域里,而不是被执行的作用域里。因此,定义作用域实际上就是定义函数。

    4.2、作用域链

    JavaScript作用域属于静态概念,根据词法结构确定,而不是根据执行确定。作用域链是JavaScript提供的一套解决函数内私有变量的访问机制。JavaScript规定每一个作用域都有一个与之相关联的作用域链。

    作用域链用来在函数执行时,求出私有变量的值。该链中包含多个对象,在访问私有变量的过程中,会从链首的对象开始,然后依次查找后面的对象,直到在某个对象中找到与私有变量名称相同的属性。如果在作用域链的顶端(全局对象)中仍然没有找到同名的属性,则返回undefined。

    【示例】通过多层嵌套的函数设计一个作用域链,在最内层函数中可以逐级访问外层函数的私有变量:

        var a = 1;                              //全局变量
        (function(){
            var b = 2;                          //第1层局部变量
            (function(){
                var c = 3;                      //第2层局部变量
                (function(){
                    var d = 4;                  //第3层局部变量
                    console.log(a+b+c+d);       //返回10
                })()                            //直接调用函数
            })()                                //直接调用函数
        })()                                    //直接调用函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在上面代码中,JavaScript引擎首先在最内层活动对象中查询属性a、b、c和d,其中只找到属性d并获得它的值(4),然后沿着作用域链,在上一层活动对象中继续查找属性a、b和c,其中找到属性c获得它的值(3),以此类推,直到找到所有需要的变量值为止,如下图所示:
    在这里插入图片描述

    4.3、函数的私有变量

    在函数体内,一般包含以下类型的私有变量:

    • 函数参数。
    • arguments。
    • 局部变量。
    • 内部函数。
    • this。

    其中,this和arguments是系统内置标识符,不需要特别声明。这些标识符在函数体内的优先级为this→局部变量→形参→arguments→函数名,其中左侧优先级要大于右侧。

    JavaScript函数的作用域是静态的,但是函数的调用是动态的。由于函数可以在不同的运行环境内被执行,因此JavaScript在函数体内内置了this关键字,用来获取当前的运行环境。this是一个指针型变量,它动态地引用当前的运行环境,具体说就是调用函数的对象。

    5、闭包函数

    闭包是高阶函数的重要特性,在函数式编程中起着重要作用。本节将介绍闭包的结构和基本用法。

    5.1、定义闭包

    闭包就是一个持续存在的函数上下文运行环境。典型的闭包体是一个嵌套结构的函数。内部函数引用外部函数的私有变量,同时内部函数又被外界引用,当外部函数被调用后,就形成闭包,这个函数也称为闭包函数。

    【示例1】下面是一个典型的闭包结构:

        function f(x){                    //外部函数
            return function(y){           //内部函数,通过返回内部函数,实现外部引用
                return x + y;             //访问外部函数的参数
            };
        }
        var c = f(5);                     //调用外部函数,获取引用内部函数
        console.log(c(6));                //调用内部函数,原外部函数的参数继续存在
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    【示例2】下面结构形式可以形成闭包,通过全局变量引用内部函数,实现内部函数对外开放:

        var c;                          //声明全局变量
        function f(x){                  //外部函数
            c =  function(y){           //内部函数,通过向全局变量开放实现外部引用
                return x + y;           //访问外部函数的参数
            };
        }
        f(5);                           //调用外部函数
        console.log(c(6));              //使用全局变量c调用内部函数,返回11
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    【示例3】除了嵌套函数外,如果外部引用函数内部的私有数组或对象也容易形成闭包:

        var  add;                        //全局变量,定义访问闭包的通道
        function f(){                    //外部函数
            var a = [1,2,3];             //私有变量,引用型数组
            add = function(x){           //测试函数,对外开放
                a[0] = x*x;              //修改私有数组的元素值
            }
            return a;                    //返回私有数组的引用
        }
        var c = f();
        console.log(c[0]);               //读取闭包内数组,返回1
        add(5);                          //测试修改数组
        console.log(c[0]);               //读取闭包内数组,返回25
        add(10);                         //测试修改数组
        console.log(c[0]);               //读取闭包内数组,返回100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    与函数相同,对象和数组是引用型数据。调用函数f,返回私有数组a的引用,即传址给全局变量c,而a是函数f的私有变量。当被调用后,活动对象继续存在,这样就形成闭包。

    注意:这种特殊形式的闭包没有实际应用价值,因为它的功能单一,只能作为一个静态的、单向的闭包。而闭包函数可以设计各种复杂的运算表达式,它是函数式编程的基础。

    如果返回的是一个简单的值,就无法形成闭包,值传递是直接复制。外部变量c得到的仅是一个值,而不是对函数内部变量的引用,这样当函数调用后,直接注销活动对象:

        function f(x){            //外部函数
            var a = 1;            //私有变量,简单值
            return a;
        }
        var c = f(5);
        console.log(c);           //仅是一个值,返回1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    5.2、使用闭包

    下面结合示例介绍闭包的简单使用,以加深对闭包的理解。
    【示例1】使用闭包实现优雅的打包,定义存储器:

        var f = function(){               //外部函数
            var a = []                    //私有数组初始化
            return function(x){           //返回内部函数
                a.push(x);                //添加元素
                return a;                 //返回私有数组
            };
        }();                              //直接调用函数,生成执行环境
        var a = f(1);                     //添加值
        console.log(a);                   //返回1
        var b = f(2);                     //添加值
        console.log(b);                   //返回1,2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在上面示例中,通过外部函数设计一个闭包,定义一个永久的存储器。当调用外部函数并生成执行环境之后,就可以利用返回的匿名函数,不断地向闭包体内的数组a传入新值,传入的值会一直持续存在。

    【示例2】在网页中,事件处理函数很容易形成闭包:

        function f(){                             //事件处理函数,闭包
            var a = 1;                            //私有变量a,初始化为1
            b = function(){                       //开放私有函数
                console.log( "a = " + a );        //读取a的值
            }
            c = function(){                       //开放私有函数
                a ++ ;                            //递增a的值
            }
            d = function( ){                      //开放私有函数
                a --;                             //递减a 的值
            }
        }
        </script>
        <button onclick="f()">生成闭包</button>
        <button onclick="b()">查看a的值</button>
        <button onclick="c()">递增</button>
        <button onclick="d()">递减</button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在浏览器中浏览时,首先单击“生成闭包”按钮,生成一个闭包。单击“查看a的值”按钮,可以随时查看闭包内私有变量a的值。单击“递增”“递减”按钮时,可以动态修改闭包内变量a的值,演示效果如下图所示:
    在这里插入图片描述

  • 相关阅读:
    【LeetCode】1154.一年中的第几天
    go 并发
    mysql8离线安装
    Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第2章 Vue.js绑定样式及案例 2.5 简单版购物车案例
    SpringAOP
    用python把docx批量转为pdf
    安装scrcpy-client模块av模块异常,环境问题解决方案
    学会“创建者模式”再去找她玩(原型)
    Web安全Day1 - SQL注入、漏洞类型
    LabVIEW代码生成错误 61056
  • 原文地址:https://blog.csdn.net/YYBDESHIJIE/article/details/134266187