JavaScript使用函数

编程入门 行业动态 更新时间:2024-10-25 05:19:34

JavaScript使用<a href=https://www.elefans.com/category/jswz/34/1771370.html style=函数"/>

JavaScript使用函数

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

1、定义函数

1.1、声明函数

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

    function funName([args]){statements}

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

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

    function funName(){}                       //空函数

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

1.2、构造函数

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

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

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

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

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

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

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

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

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

【示例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")

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

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

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

1.3、函数直接量

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

    function([args]){statements}

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

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

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

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

    var f = function(a, b){return a + b;};

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

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

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

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

1.4、箭头函数

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

    (param1, param2, …, paramN) => { statements }(param1, param2, …, paramN) => expression

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

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

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

    (singleParam) => { statements }                //正确singleParam => { statements }                  //正确

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

    () => { statements }

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

    var fn = x => x * x;

等价于:

    var fn = function (x) {return x * x;}

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

    var fn = (x,y) => {if (x > y) {return x;} else {return y;}}

2、调用函数

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

2.1、常规调用

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

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

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

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

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

2.2、函数的返回值

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

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

    function f(){var a = [];a[0] = true;a[1] = 123;return a;               //返回多个值}

在上面代码中,函数返回值为数组,该数组包含两个元素,从而实现使用一个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;}

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);                 //1obj.increment(2);console.log(obj.value);                 //3

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

2.4、动态调用

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

    function.call(thisobj, args...)function.apply(thisobj, [args])

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

在上面示例中,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

如果把一个数组或伪类数组的所有元素作为参数进行传递,使用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

在上面示例中,设计定义一个函数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 );                //调用系统函数maxconsole.log( m );                                   //返回63

2.5、实例化调用

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

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

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

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

3、函数参数

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

3.1、形参和实参

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

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

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

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

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

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

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

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

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

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

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

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

现在可以设置:

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

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);                                          //逐个显示每个传递的实参

注意: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

【示例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

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

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

3.5、剩余参数

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

    function(a, b, ...args) {//函数体}

如果函数最后一个形参以…为前缀,则它表示剩余参数,将传递的所有剩余的实参组成一个数组,传递给形参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));

可以简写为:

    var fn = (...rest) => {var i, sum = 0;for (i=0; i<rest.length; i++) {sum += rest[i];}return sum;}

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})()                            //直接调用函数})()                                //直接调用函数})()                                    //直接调用函数

在上面代码中,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));                //调用内部函数,原外部函数的参数继续存在

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

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

【示例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]);               //读取闭包内数组,返回1add(5);                          //测试修改数组console.log(c[0]);               //读取闭包内数组,返回25add(10);                         //测试修改数组console.log(c[0]);               //读取闭包内数组,返回100

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

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

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

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

5.2、使用闭包

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

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

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

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

    function f(){                             //事件处理函数,闭包var a = 1;                            //私有变量a,初始化为1b = 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>

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

更多推荐

JavaScript使用函数

本文发布于:2023-11-15 07:59:11,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1596315.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:函数   JavaScript

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!