JavaScript函数

目录

函数定义

函数分类

函数参数

传递参数方式

参数异常的情况

参数同名

arguments类数组

arguments方法

定义一个函数

函数的声明提升

函数的重复定义

函数的嵌套定义

函数返回值

为什么需要返回值

怎样使函数返回某个值

使用返回值时注意

 函数实例部分

函数的调用

函数的递归调用

函数this

特殊类型的函数

构造函数

作为值的函数

匿名函数

立即执行函数(IIFE)

函数的属性和方法

函数作用域

Javascript预定义函数


函数定义

函数是一段可以被重复执行或调用的代码块。

当某一段代码使用次数较为频繁时,使用函数将这一段代码简化为一个函数,只需要函数名就可以调用这一整段代码,可以减少程序员工作量,使开发工作更有条理性。

eg:

    //关键词function声明函数,类似于var声明变量
    //add_two_nums为函数名,可以使用小驼峰命名法和下划线分割单词等命名规范,
    //小括号内为函数参数,
    //{}大括号内是函数体
    //return 指定函数返回值
    function add_two_nums(num1,num2){
      var result=num1+num2;
      return result;
    }

函数分类

函数分为用户自定义和系统自带两类

用户自定义函数:function add(num1,num2){······}

预定义函数:alert(),Math对象的方法,parseInt(),isNaN()······

函数参数

函数定义时会在函数名后的小括号内书写至少0个参数。要求参数间以逗号分隔,

参数类型不限,命名遵循标识符规则,写在小括号内的参数就是函数的形式参数。形式参数相当于定义在函数内部的临时(局部)变量。函数执行时把实际参数(实参)的值传递给形参。函数在执行完成后如果没有方式访问函数内部的参数,这些参数将会被视为内存垃圾,被垃圾回收机制自动清除。

传递参数方式

函数参数分为基础数据类型和复合数据类型(对象,正则表达式,函数等)

基础数据类型的参数传递是直接传值。实际参数的值赋给形式参数。形式参数在函数运行过程中可以修改,对实际参数无影响。

复合数据类型的参数传递是地址的传递,也可以说是引用方式的传递。仍然只有一个对象,在函数中执行的对参数的修改会修改内存中的参数对象,这一点值得注意。

参数异常的情况

javascript调用函数时不会检查函数参数个数,所以会出现函数实际参数比形式参数多或少的情况。

当调用函数时实际参数少于形式参数时,在实际参数传递值给形式参数时,由于没有实际参数,会被赋值为undefined,最终只有部分参数被赋值,其余参数为undefined。我们使用的一部分对象方法的参数可以省略,就是检测到对应的形式参数传值后为undefined,再根据不同参数的省略情况执行不同代码。这样扩展了函数的功能和实用性。

当调用函数时实际参数多于形式参数时,前面几个参数可以被正常的传值,后面的参数的值会传入到一个arguments的类数组。

参数同名

大概这类问题只会在面试题中出现,正常工作中不会出现。出现同名参数时函数调用的参数的值是后一个传入的参数值。

    function f(a,a){
      console.log(a);
    }
    f(1,2);//这里输出2
    //可以理解为后一个a=2,把前一个a=1覆盖了
    //实际想要调用这两个参数时建议使用arguments类数组

arguments数组

函数的参数会传值到一个叫做arguments的类数组。这个类数组用于保存函数参数,我们可以使用参数名访问部分变量,也可以使用arguments[0]访问第一个参数,arguments[1]访问第二个参数······

arguments这个类数组在函数执行时产生,不论函数参数个数多少都会保存。值得注意的是这个是数组,具有length属性,但不具有push等数组方法。

arguments方法

arguments这个类数组具有caller和callee两个方法,这两个方法作用一样,但caller已经在更新中被放弃,callee仍可用。作用是指向函数本身。在函数中调用这个方法可以在函数内部调用函数本身。可以用于函数的递归调用(函数执行中再次调用自身)。

注意:在ES5严格模式下不能使用callee这个方法

定义一个函数

函数使用function关键词定义。定义一个函数包括函数名,函数体,函数参数部分。

  • 函数名,命名可以采用add_twonums 以动词开头,下划线分割不同单词;也可以使用addNums第一个单词首字母小写,其余大写。函数名可以不写,省略就成了匿名函数,匿名函数没有函数名,调用时一般把函数赋值给某个变量,使用变量名调用函数。
  • 函数参数:函数名后跟小括号(),小括号内就是函数参数,函数参数数量为至少0个。函数参数使得函数功能可以根据函数参数的不同而变化,大大拓宽了函数的作用。
  • 函数体:在小括号后是大括号{}。函数体就写在大括号内。函数体的具体内容就是函数封装的具体功能。

我们也可以使用Function这个构造函数进行函数的创建。

var add=new Function("num1","num2","return num1+num2");

Function()的可以有任意个参数。

这任意个参数中,最后一个参数是add函数的函数体,前面的参数都是add函数的参数。

这个使用构造函数创建的add函数为

function add(num1,num2){
return num1+num2; 
}

一般由于这个方式创建函数比较繁琐 ,平时使用较少。

函数的声明提升

javascript预编译过程中发生变量声明提升和函数整体提升,函数在执行前会把定义部分提前,我们可以在函数定义前就调用函数,这不会报错。

    f();
    function f(){
      console.log("hello world");
    }

函数的重复定义

函数定义重复时后面定义的函数会覆盖前面定义的同名函数。变量的重复声明不会影响变量的使用,而函数的重复定义会改变函数,函数最最终为后定义的函数。

eg:

    console.log(add(2,1));//输出结果为1
    function add(num1,num2){
      return num1+num2;
    }
    function add(num1,num2){
      return num1-num2;
    }

 两个函数同名,后一个函数成为最终add表示的函数,实质上进行的是2-1=1的运算。

函数的嵌套定义

js中函数可以嵌套定义。一个函数内部可以定义其他函数并加以调用。嵌套定义时函数中声明的变量作用域相互嵌套,使得javascript功能更加强大。常见实例有window.οnlοad=function(){}。这句代码可以使js代码在DOM树建立完成后再执行,避免js代码执行过早,html元素还未出现就执行js代码,导致js获取元素失败。

函数返回值

为什么需要返回值

函数被调用并执行后结束,其中的变量等内容会因无法访问被垃圾回收机制处理。如果函数的作用只是输出固定内容等,这样的函数就不需要返回值。如果函数对数据进行处理后还需要对处理后的数据进行进一步处理,这时就需要把运算结果保留到函数外部并交给其他代码处理。这时我们需要使用函数返回值。

怎样使函数返回某个值

使函数返回某个值需要return关键词后跟需要返回的内容。返回值可以是基本数据类型,字符串,数字,布尔类型,null或undefined,也可以是对象。这个对象包括数组,正则表达式,函数,新建对象等内容。

使用返回值时注意

在return语句执行后,函数的执行就正式结束,不管后面还有没有其他语句。return后不写时返回undefined

 函数实例部分

        function add(num1,num2){
			return num1+num2;
		}
		console.log(add(1,2));//输出3

 这里使用返回值得到两者的和。

函数的调用

函数在定义后需要被调用才会执行函数体内部的代码。函数调用需要函数名和小括号()

一般组成为函数名()。函数如果有参数的话需要在小括号内部填写参数,参数间以逗号分隔,填写的参数就是函数的实参(实际参数)。

针对对象的方法,对象的方法也是一个函数,执行该函数需要调用该方法,一般方式为objname.function_name()。与直接调用函数类似。

函数的递归调用

在执行过程中调用它自身的函数我们称为递归函数,递归调用使得函数体更加精简。我们求阶乘的函数,求斐波那契数列的函数,都可以使用递归函数完成。

eg:斐波那契数列

    function fbnq(n){
      if(n==1||n==2){
        return 1;
      }else if(n>=2){
        return fbnq(n-1)+fbnq(n-2);
      }
    }
    console.log(fbnq(5));

阶乘函数

    function factorial(n){
      if(n==1){
        return 1;
      }
      else{
        return n*factorial(n-1);
      }
    }
    console.log(factorial(10));

值得注意的是,递归函数需要达到某个程度后停止运行。例如上面的n=1,n=2时, 返回值不再调用函数,而是直接返回某个值。如果函数没有指定到达某种情况停下,递归函数就会不断递归调用,不会停止。

		function fun1(){
			console.log("这是函数1,直接调用。");
		}
		var obj={
			name:"函数2",
			say_name:function(){
				console.log("这是调用对象的方法"+this.name);
			}
		}
		var arr1=[function(){
			console.log("这是数组中的第一个函数");
		},
		function(){
			console.log("这是数组中的第二个函数");
		}];
		//函数调用部分
		fun1();
		obj.say_name();
        obj["say_name"]();//这里是另一种访问对象属性或方法的方式
		arr1[0]();
		arr1[1]();

函数this

this不是一个函数,对象,也不是一个变量,而是一个关键词。这个关键词不可赋值,指向调用该方法的对象。

        var obj={
			name:"函数1",
			say_this:function(){
				console.log(this);
			},
			father:{
					name:"对象名",
					son:function(){
					console.log(this);
				}
			}
		}
		obj["say_this"]();//输出对象obj
		obj.father.son();//输出对象father

函数嵌套层数过多时,为了下一层次的函数可以使用上一层次的调用对象,我们可以使用变量保存this。

特殊类型的函数

构造函数

构造函数在调用前需要加上关键词new,我们可以看到js内置的构造函数有Object,Number,Function,Array,RegExp,Document等。构造函数执行时会新建一个对象,这个对象继承构造函数的prototype属性,构造函数中this关键词指向这个新建的对象,在构造函数中我们使用this为新建对象赋值。

eg:

        function Person(){
			this.name="新建对象";
		}
		var per1=new Person();
		console.log(per1);//这里输出的就是新建的对象
		//为了使构造函数的功能更加强大我们可以引入参数来扩大函数功能。
		function Person2(name,age){
			this.name=name;
			this.age=age;
			return this;
		}
		per1=new Person2("新建对象",0);
		console.log(per1);

作为值的函数

javascript中的函数实质上也是对象,可以作为值赋给变量,也可以作为对象的属性。 作为对象的属性时函数就成了对象的方法,作为值赋给某个变量时,我们就可以通过这个变量来调用这个函数。通过赋值给变量再调用函数对于匿名函数来说,可以增加调用函数的方式。

匿名函数

匿名函数:顾名思义,就是没有函数名的函数。这类函数定义时没有声明函数名,再次调用时会有一点不方便,一般可以赋值给变量来调用。

匿名函数优点

当函数只需要执行一次时”不方便调用“就不再是个问题,不需要函数名反而能够节省命名空间。

定义方式1

function(num1,num2)
{
    return num1+num2;
};

一般为了便于调用会把这个函数赋值给一个变量

var add=function(num1,num2)
{
    return num1+num2;
};

定义方式2:

(function(num1,num2)
{
    console.log(num1+num2);
    return num1+num2;
})(1,2);

这里匿名函数定义后立即执行,并且之后将不能再被调用。

立即执行函数(IIFE)

立即执行函数(Immediately-Invoked Function Expression)在定义后立即执行。

 (function f(num){console.log(num);})(1);
 (function s(str1){console.log(str1)}("hello world"));

以上定义了两个立即执行函数,这两个立即执行函数是常见的立即执行函数格式。

注意使用()把函数定义部分(函数参数可有可无)括起来。

javascript关于函数function有如下机制:function在一行代码开头时,被认为是语句,否则被认为是表达式。

   function f(){//function作为语句
     /*code*/
   }//
   var s=function ss(){/*code*/}//function作为表达式

function作为表达式时,可以直接和()组合调用 。eg:

var f=function(num){alert(num)}(1);

实际上,只要不让function作为一行代码的开头,就可以定义函数再直接调用。

   true &&function f(num){console.log(num)}(1);
   !function f(num){console.log(num)}(2);
   +function f(num){console.log(num)}(3);
   -function f(num){console.log(num)}(4);
   ~function f(num){console.log(num)}(5);

  以上内容参考这里 。

函数的属性和方法

函数也是对象的一种,具有多个属性和方法。

name 返回函数的函数名
length 函数形式参数个数
toString() 返回函数的源码
    var ff=function f(){
      console.log("hello world");
    }
    console.log(ff.name);//输出为f
    var ss=function(){
      console.log("???");
    }
    console.log(ss.name)//匿名函数直接输出变量名
    function add(x,y){
      return x+y;
    }
    console.log(add.length);//输出为2
    function nums(num1,num2,num3){
      return num1+num2+num3;
    }
    console.log(nums.length);//输出为3
    console.log(nums.toString())//输出函数的源码

函数作用域

作用域(scope):是变量作用的范围。ES5中javascript包含全局作用域和函数作用域,ES6中还有块级作用域。

全局作用域:直接在script标签中声明的变量就在全局作用域中。这里要求变量不能在某个函数内部声明。也可以是在函数内部直接赋值,没有使用var关键词声明的变量(暗示全局变量)。全局作用域的变量都是window的属性。var a=10等价于window.a=10。

函数作用域:在函数内部没有使用var关键词声明的变量,作用域就是全局作用域,如果使用var关键词声明,作用域就是函数作用域。

块级作用域:使用let关键词声明变量,声明的变量只能在当前大括号{}中生效,变量声明不再提升,且不可重复声明。这个大括号包括函数,if(){},for(){}。

    if(true){
      // console.log(age);由于声明不再提升,这里输出会报错。
      var name=1;//var声明的变量在全局作用域中
      let age=2;//在{}中且使用let声明,在块级作用域中
      // let age=3;块级作用域中重复声明一个变量会报错
    }
    console.log(name);//正常输出1
    console.log(age);
    //输出age时报错,age这个变量使用let关键词声明,作用域是块级作用域,只在{}内部生效。
    //总结如下1. let不能重复声明
    //2. let声明的变量不会在预编译阶段声明提升
    //3. let声明的变量只会在{}内生效

tips:变量声明的几个关键词

1. var 在函数中使用就是声明函数作用域中的变量,全局中使用是全局作用域中的变量。

2. let 在{}中使用声明块级作用域中的变量,没有声明提升,不可重复声明,不能跨作用域访问。

3.const 声明变量时必须赋值,在后续代码中不可修改变量的值,只在当前{}块中生效,不能跨块访问。

4.同一个变量不能使用多个关键词声明。

Javascript预定义函数

javascript具有很多预定义函数。

eval() 对参数中的js代码求值
isFinite() 有限数字返回真,无限或NaN返回假
parseInt() 对参数字符串求整数,只是返回前几位数字组成的整数
parseFloat() 对参数字符串求浮点数,类似于parseInt()

笔者也在学习javaScript中,本文为学习内容的整理笔记。如有错误,欢迎留言指正。

你可能感兴趣的:(javascript学习笔记,javascript,前端)