JavaScript基础三:函数

96 阅读9分钟

函数概述:

  1. 函数是由事件驱动或者当它被调用时执行的可重复的代码块;即:将特定功能的代码封装到一个功能模块中,当需要这个功能时,直接调用这个功能模块的接口,也就是封装后的函数名,这样可以减少大量重复代码的书写,达到优化程序的目的。Js中的函数分为全局(内建)函数和自定义函数,目前讲的主要时自定义函数;
  2. 常见的全局函数有:parseInt()、parseFloat()、eval()、isNaN()、encodeURI()、decodeURI()

函数的定义有两种方式:

普通定义和变量定义(面向对象讲),这里主要介绍普通函数的定义方式。语法如下: function 函数名(参数列表){ 函数体; }

这里,函数名为自定义标识符,命名类似于变量;参数列表为形式参数,方法调用结束后消失;函数体即操作语句;函数调用时会为形参提供实参的值。

参数分为形式参数(形参parameter)和实际参数(实参arguments),形参相当于函数定义时起的变量,实参则是调用函数时传入的数据。js中实参和形参的匹配 没有java中那么的严格:如果实参多于形参,则多余参数自动被忽略;如果实参小于形参,则用undefined补足缺少的实参值。

案例:创建一个简单的函数,实现两个数字相加的和

函数的调用:

函数定义好之后,是不能自动执行的,所以需要调用它

  1. 事件发生时调用(如点击事件)
  2. js代码中直接调用
//实现五组两个数的和
/* var a = 10,b=20;
alert(a+b);
var c = 20,d=20;
alert(d+c);
...代码量大麻烦 */
			
sum(10,20);//这里将实参传入函数中替换形参,得到实参根据计算规则计算后的结果
sum(20,30);//可以调用任意多次数,较上面代码大大优化
function sum(a,b){
	alert(a+b);
}

带参数(argument)的函数:

  1. 在调用函数时,您可以向其传递值,这些值被称为参数;
  2. 这些参数可以在函数中使用;
  3. 您可以发送任意多的参数,由逗号 (,) 分隔。
  4. 当您声明函数时,请把参数作为变量来声明;
  5. 调用含参函数时,变量和参数必须以一致的顺序出现。第一个变量就是第一个被传递的参数的给定的值,以此类推。
function demo(name,age){
    alert("您好!我叫"+name+",今年"+age+"岁");
}

思考:

之前函数中,都是把结果直接输出来,那么如果想对函数的结果进行处理怎么办呢?这里就用到了返回值函数返回值函数:有时我们需要将函数的值返回给调用它的地方,这个时候就可以通过return语句实现。语法如下:

  • 语法 return 表达式;
  • 功能 返回表达式的值
  • 注:
    • 如果是仅仅希望退出函数,也可使用return语句,见案例2;
    • 函数中参数和返回值不只是数字,还可以是字符串等其它类型。

变量分为全局变量和局部变量:

全局变量和局部变量变量名相同时调用变量:就近原则

  1. 子级作用域可以访问父级作用域的局部变量,但父级不能访问子级作用域
  2. 不通过var声明的变量会称为全局变量(建议使用var声明) 垃圾回收机制:和java一样,js同样具有垃圾回收机制,释放不需要的内存
var n = 10; //全局变量,任何位置都可以使用,不会被系统回收
function test(){
	var i = 30; //局部变量,只能在当前函数中使用,函数调用结束即销毁,会被系统回收释放内存
	console.log("i="+i); 
}
test();
//console.log(i);  //Uncaught ReferenceError: i is not defined
console.log("n="+n);  //10
	
	
//全局变量和局部变量变量名相同时调用变量:就近原则 
var a = 50;
function num(){
	var a = 20;
	console.log("num="+a);
}
		
num();

匿名函数:没有名字的函数

  • 基本形式:(function(){...})(); 前面括号包含函数体,后面括号给匿名函数传递参数并立即执行之
  • 作用:产生一个局部作用域,避免全局变量的污染以及函数名的冲突;
  • 小括号的作用
    • 小括号能把我们的表达式组合分块,并且每一块,也就是每一对小括号,都有一个返回值。这个返回值实际上也就是小括号中表达式的返回值。所以,当我们用一对小括号把匿名函数括起来的时候,实际上小括号对返回的,就是一个匿名函数的Function对象。因此,小括号对加上匿名函数就如同有名字的函数被我们取得它的引用位置了。所以如果在这个引用变量后面再加上参数列表,就会实现普通函数的调用形式。
    • 简单来说就是小括号有返回值,也就是小括号内的函数或者表达式的返回值,所以说小括号内的function返回值等于小括号的返回值
//调用函数,得到返回值。强制函数直接量执行再返回一个引用,引用再去调用执行
(function(x,y){
    alert(x+y);
    return x+y;
})(10,20);
		
// 调用函数,得到返回值。强制运算符使函数调用执行
(function(x,y){
    alert(x+y);
    return x+y;
}(10,20));

递归函数描述:

所谓递归函数就是在函数体内调用函数本身(说白了就是自己调用自己);使用递归函数一定要注意,处理不当就会进入死循环。递归函数只在特定的情况下使用 ,比如阶乘问题

//求10的阶乘 10*9*8*...*3*2*1
var num = 1;
function jiecheng(m){
    if(m == 1){
        return 1;
}
    return m*jiecheng(m-1);
}
		
console.log(jiecheng(10));

闭包就是能够读取其他函数内部变量的函数。

例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

  1. 闭包三大特点:
  • 函数嵌套函数
  • 内部函数可以访问外部函数的变量
  • 参数和变量不会被回收
  1. 对于初学者比较难理解,可以通过以下两句话来加深理解:
  • 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量。 闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。
  • 不必纠结到底怎样才算闭包,其实你写的每一个函数都算作闭包,即使是全局函数,你访问函数外部的全局变量时,就是闭包的体现。
function test(){
    var i = 10;
    return function(){
        alert(i);
    }
}
		
    var a = test();
    a();
		
//变量a在test方法外部是无法访问的,但test方法里面,嵌套了一个匿名函数,通过return返回,test作用域中的变量a,
//可以在匿名函数中访问。并且当test方法执行后,变量a所占内存并不会释放,以达到嵌套的函数还可以访问的目的。

回调函数:

被当作参数传递的函数就是回调函数

function fn(backFn){
    if(true){
        backFn();
    }
}
		
fn(function(){alert(1)})

工厂模式

var stu1 = {
    name:"张三",
    age:23
}
		
var stu2 = {
    name:"李四",
    age:24
}
		
//针对以上同种属性创建的方法(已弃用) 
function factory(name,age){
    return{
        name:name,
        age:age
    }
}
		
var stu3 = factory("王五",25);
console.log(stu3);

异常捕获与抛出

  • 异常:当JavaScript引擎执行Js代码时,发生了错误,导致程序停止运行;
  • 异常抛出:当异常产生,并且将这个异常生成一个错误信息。

初级开发人员往往很少使用js的抛出和捕获异常,但抛出和捕获异常往往是非常必要的,如果学过java开发的,对于这样的抛出和捕获异常会比较熟悉。

下面就来介绍下如何使用js的抛出和捕获异常。捕获异常的语法

  • 第一种:
try{
    //运行代码
}catch(err){
    //处理错误
    console.log(err); //错误信息
}
  • 第二种:
try{
    //运行代码
}catch(err){
    //处理错误
    console.log(err); //错误信息
}finally{
    //文件的关闭,标记的取消等等
}

try块:包含的是可能产生异常的代码,在这里面直接或者在里面通过调用函数里间接抛出的异常都可以捕获到。部分浏览器还可以找到具体抛出的位置;

catch块:捕获异常,并处理异常的地方,包括条件捕获和非条件捕获;

  • 条件捕获,如 catch(e instanceof obj) 的形式,用 instanceof 判断异常的对象类型,实现指定的异常处理方式。

  • 非条件捕获,如 catch(e) 的形式,当异常抛出时,无论异常的类型都进行捕获并处理。

这里有两点注意,如果条件捕获和非条件捕获共用,那么非条件捕获必须放在最后,因为它是无条件的捕获类型,捕获后会忽略后面的任意 catch 块。另外,对异常的处理必须考虑周全,在 catch 里要么处理所有的异常,要么再次抛出异常(假定外层还有异常处理),否则在调试过程中会非常困难,因为出现的异常被忽略了

finally块:无论是否捕获异常,都会在 try 或 catch块后立即执行;finally块常常用以文件的关闭,标记的取消等操作,更多的时候作为一种 ”优雅的失败“ 而存在,常常代替 catch 块

在JS的DOM对象中,还有一个 window.onerror 作为事件监听来从全局处理JS运行时的错误。由于浏览器的差异,实际上许多错误事件不能触发 window.onerror ,因此要小心使用抛出异常的语法throw语句:JS里用throw指定一个用户定义的异常类型, 并抛出。抛出点可以在 try 或 catch 块中(一般都是配合try...catch使用)

var sub = document.getElementById('submit');
sub.addEventListener("click",rex);
function rex(){
    try{
        //自定义异常抛出 
        var username = document.getElementById('username').value;
        if(username == ""){
            throw("输入的用户名为空");//抛出异常
        }
    }catch(e){
        //TODO handle the exception
        alert(e);//弹出异常信息
    }
}