Skip to content

在程序中,我们可能有很多重复的功能,比如我的程序中可能经常需要遍历一个数组,那如果我每次遍历数组都去写一个 for 循环,这显然是低效的,也很难维护,为了解决这个问题,就有必要引入函数。

可以说,函数最大的功能就是用来减少重复代码的。我们可以通过以下语法创建一个函数。

js
function 函数名(){
	函数体;
}

这里的函数体就是一个代码块,以上这种写法,我们称为函数字面量

我们举个例子来说明

js
function test(){
	console.log("==============================");//这是一个分隔符
}

test();//调用这个函数,打印了一个分隔符
test();//调用这个函数,又打印了一个分隔符

以上就创建了一个名为 test 的函数,这个函数的作用,就是用来输出一个分隔符,当我们需要使用分隔符的时候,只需要调用这个函数即可,不需要再去写 console.log。

从上面的例子可以看出,当我们创建一个函数之后,我们就可以随时使用这个函数。使用函数的方式也很简单,函数名加上一个小括号即可。这样,每次需要这个功能的时候,我们只需要使用这个函数即可,这样就可以大大减少重复代码。

通过形如 function 函数名(){} 这种方式定义的函数,会提升到脚本块的顶部,这一点和变量类似。同时,也会成为全局对象 window 的属性。

js
test();
function test(){
	console.log("==============================");//这是一个分隔符
}

上例中,函数是可以顺利执行的。

请注意,这里回顾一下变量的提升,对于 var a = 1; 这样一个语句,实际上是 var a; 和 a = 1; 两条语句的复合,会提升的只有 var a;,而后面的赋值语句是不会提升的。

值得注意的是,不管是函数还是变量的提升,都只会提升到当前脚本块的顶部,看一个例子

html
<script>
test();//报错
</script>
<script>
function test(){//只会提升到当前脚本块的顶部
	console.log("==============================");//这是一个分隔符
}
</script>

在上例中,test 函数的定义只提升到第二个脚本块的顶部,而第一个脚本块中对 test 函数调用的时候,还看不到 test 函数的定义,所以会直接报错。

接下来再看

js
function test(){
	console.log("test");
}
window.test();//正确输出了"test"

如果使用 typeof 函数名,得到的结果会是一个 "function"

JS 是函数级作用域语言,什么意思呢,就是在函数内部声明的变量,只能在函数内部使用。

js
function test(){
	var a = 1;
}
test();
console.log(a);//这里并不能正常输出。

但如果不使用 var 关键字,则是暗示全局变量,相当于给 window 对象直接添加属性。

js
function test(){
	a = 1;
}
test();
console.log(a);//这里可以正常输出。

如果在函数内部使用 var 声明变量,变量会提升到当前函数的顶部

js
function test(){
	console.log(a);//并不会报错
	var a;//这行代码提升到函数的顶部
}
test();

函数内定义的变量只会在函数内部生效,这意味着,我们可以在函数内部和函数外部定义同名变量,并不会冲突,函数外部使用函数外部的变量,函数内部则使用内部的变量。

接下来我们写一个函数,用于判断一个数是否是奇数

js
function isOdd(){
	var n = 123;
	if(n % 2 == 0){
		console.log("不是奇数")
	}else{
		console.log("是奇数");
	}
}
isOdd();

此时我们可以调用这个函数,判断 123 是不是奇数,但是这显然很扯淡,因为 var n = 123 是写死的,我的程序中可能会大量使用到这个功能,但肯定不可能每一次都判断 123 是不是奇数,我需要实现的效果是,我给他一个数字,他就能判断出这个数是不是奇数。这个时候这个 n 就不能写死,而是由我给他。为了实现这个效果,引入参数的概念。

js
function isOdd(n){
	if(n % 2 == 0){
		console.log("不是奇数")
	}else{
		console.log("是奇数");
	}
}
isOdd(4);
isOdd(5);

在以上例子中,我将 n 写在了创造函数时的括号里面,这个操作相当于在函数最开始的时候使用 var n 创建了一个变量,但是不着急赋值,在使用 isOdd 这个函数的时候,括号里传入了这个数字,相当于就把函数内部的 n 赋值给了我指定的数字,此时这个函数就可以实现判断指定数字是不是奇数的效果。

定义函数的时候写在括号里的变量我们称为参数,可以理解为函数中未知的数据,或者说是调用函数的时候传入的数据。写在函数定义时的参数成为形式参数,而调用函数的时候传入的参数,例如上例中的 4 和 5,称为实际参数。所以,我们使用参数,将函数的内部和外部联系起来。

参数可以有多个,多个参数使用逗号隔开,例如下例

js
//该函数用于计算两个数的和
function sum(a,b){
	console.log(a+b);
}
sum(1,2);//输出3

参数本质上就是函数内部的变量,只不过这个变量很特殊,只是一个占位符,函数定义的时候并不知道它实际的值是多少,只有当函数被调用的时候,通过在括号里传入参数的方式,参数才会获得实际的数据。

我们可以认为,参数的存在,可以让函数内部可以使用到函数外部的数据。但反过来呢,我怎么让函数外部拿到函数内部的数据呢,我们可以看到,在前面的例子中,我们的结果都是直接输出,但这显然不对,你只能输出,函数外面拿不到最终结果,这个时候就需要 return 关键字。

js
//该函数用于计算两个数的和
function sum(a,b){
	return a+b;//返回a+b的数据
}
console.log(sum(1,2));//此时函数外部就可以拿到函数内部返回的结果

如果实参没有传递,对应的形参为 undefined

接下来做一个例子,一个数组里有很多个数,我现在写一个函数对这个数组求和,返回结果。

js
function sum(arr){
	var s = 0;//用于保存结果
	for(var i = 0 ;i<arr.length;i++){//遍历数组,求和
		s += arr[i];
	}
	return s;//最后的结果返回
}
var arr = [1,4,6,66,2,34,8];//一个数组
var result = sum(arr);//调用函数,得到结果
console.log(result);

读者可以这样理解,任何一个表达式都会有一个返回值,这个前面介绍过,函数调用,也就是上例中的 sum(arr),这个也是一个表达式,也会有一个返回值,函数的返回值就是创造函数内部的 return 后面的东西,如果没有写 return,或者 return 后面不写任何内容,那么返回值就是 undefined。

接下来考虑一个比较神奇的问题

js
function test(a){
	a++;
	console.log(a);
}
var a = 1;
test(a);
console.log(a);

在上面的例子中,在函数内部将 a 加一,但是函数外部的 a 并没有改变。实际上,函数在进行参数传递的时候传递的是值,将 a 传递给 test 函数的时候,实际传递的是 1 这个数值,而不是变量 a。

值得注意的是,return 语句会直接结束整个函数。

接下来实现一个功能,输入任意年份,输出这一年每个月有多少天

js
/**
       * 获取指定年份每个月的天数
       * @param {number} year - 需要查询的年份
       * 该函数没有返回值,它会在控制台打印出每一年每个月的天数。
       */
        function getDay(year) {
            // 判断是否为闰年
            var isLeapYear = (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
            // 每一个月的天数(非闰年2月为28天)
            var days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            // 如果是闰年,则将2月的天数改为29天
            if (isLeapYear) {
                days[1] = 29;
            }
            // 循环输出每个月的天数
            for (var i = 0; i < days.length; i++) {
                console.log(`第${i + 1}月有${days[i]}天`);
            }
        }
        // 调用函数,查询2019年每个月的天数
        getDay(2019);

以笔记为镜,可以知得失