一.初步了解函数
1. 函数的定义和调用
1.1 函数定义
函数其实就是将多条语句,组合成一个“语句军团”,集体作战。
//定义一个函数,函数就是一组语句的集合
function fun(){
console.log(1);
console.log(2);
console.log(3);
console.log(4);
}
//调用函数
fun();
函数必须先定义然后才能调用.
定义一个函数,用关键字function来定义,
function后面有一个空格,后面就是函数名字,函数的名字也是标识符,
既然是标识符,那么命名规范和变量命名就是一样的。
定义一个函数,也叫声明一个函数:
function 函数名(){
}
函数如果不调用,那么里面的语句就一辈子不会执行,不调用就等于白写。
1.2 函数调用
调用一个函数的方法非常简单,函数名后面加一个() ,
() 是一个运算符,表示执行一个函数。
执行函数:
函数名()
一旦调用了函数,函数内部的语句就会执行。
2. 函数的参数
2.1 参数的了解
可以通过参数让函数内部的语句有所不同
定义函数的时候,内部语句可能有一些悬而未决的量,就是变量,这些变量,我们要求在定义的时候都罗列在小括号中:
比如:
function fun(num){
console.log("这已经是我第" + num + "次说爱你们了,我哭了,你们呢?");
}
调用的时候,要把这个变量的真实的值,一起写在括号里,这样随着函数的调用,这个值也传给了num:这就是参数传递
执行这个函数:
fun(99);
罗列在function小括号中的参数,叫做形式参数;
调用时传递的数值,叫做实际参数。
形式参数就像占位置,先把位置站好,等你来赋值
2.2 参数个数
参数可以有无数个,用逗号隔开。
//有多少形式参数都可以,都罗列出来
function fun(a,b){
console.log(a + b);
}
fun(2,6); //输出8
fun(6,18); //输出24
定义函数的时候,不需要指定类型:
function sum(a,b){
console.log(a + b);
}
也就是说调用的时候,传进去值是什么类型,就是形参a、b就是什么类型
sum("5",20);
输出520,做的是连字符的运算。
2.3 实参和形参个数不等
我们可以发现,声明函数的时候和调用函数的时候参数个数可以不一样多,也不报错。
- 实参个数小于形参个数
sum(20); //NaN
因为我们只传了一个参数,b就没有传递,b被隐式的var了,所以值undefined。10+undefined就是NaN
也就是说没有实参传递的形参是undefined.
- 实参个数大于形参个数
sum(10,20,32,23,22,2,4); // 30
只有前两个参数被形参接收了,后面的参数无视了,
也就是说,形参会按顺序接收实参,多余的实参,没人接收,也就不了了之了
2.4. arguments
每个函数里面都有一个隐式的arguments这个是系统以及给你创建好的,每个函数内部都有,arguments是类似于数组的类数组.就是实参列表
类数组本质上是一个对象,什么是类数组先不用管,先把他当成一个数组,涵盖了所有实参。
调用函数的时候,比如:
fun(45,43,57,347,223,162);
此时函数内部,arguments就有一个下标,就依次等于上面调用的数:
arguments[0] // 45
arguments[1] // 436
……
arguments[5] //12
如果函数里面有形式参数列表,那么是和arguments同步的:
function fun(a,b){
arguments[0] = 8; //改变了第一个参数的值
alert(a); //8 ,弹出改变后的值
console.log(arguments.length); // 实参的长度
console.log(fun.length); // 形参的长度
}
fun(45,43,57,347,223,162);
arguments的功能,是模拟函数的重载,使得同一个函数,根据参数个数的不同,有不同的作用。
3. 返回值
函数可以通过参数来接收东西,更可以通过return的语句来返回值,“吐出”东西。
function sum(a,b){
return a + b; //现在这个函数的返回值就是a+b的和
}
console.log(sum(5,4)); //sum没有输出功能,就要用console.log输出
//sum(5,4)实际上就成为了一个表达式,用来计算结果
//计算后就是9,相当于console.log(9);
函数只能有唯一的return,有if语句除外,因为if是分支语句
程序遇到了return,将立即返回结果,返回调用它的地方,而函数内return后面的语句将不在执行。
function fun(){
console.log(1);
console.log(2);
return; //返回一个空值
console.log(3); //这行语句不执行,因为函数已经return了,所以不会打印3
}
fun(); //1, 2
3.2 函数作为参数使用
函数有一个return的值,那么现在这个函数,实际上就是一个表达式,换句话说这个函数调用完成后整体就是一个值。
所以这个函数,可以当做其他函数的参数。
sum(5,sum(5,2)); //12
程序是先执行内层在执行外层.
函数可以接受很多值,返回一个值,
函数的意义:
- 在出现大量程序相同的时候,可以封装为一个function,这样只用调用一次,这样只要写一次代码就可以调用多次,减少代码耦合。
- 我们在调用一个函数的时候,不用关心函数内部的实现细节,只要可以运用。并能给给我们团队开发带来了好处。
- 模块化编程,让复杂的逻辑变得简单。
4. 递归
函数内部又调用了函数自身,我们把这种情况叫做递归
斐波那契数列就是经典的递归算法:
1、1、2、3、5、8、13、21、34、55、89、144、233……
输出斐波那契数列
欣赏,对于一个搞前端的来说,没必要把程序学这么深:
//1、1、2、3、5、8、13、21、34、55、89、144、233……
//只需要一个函数,就可以搞定全部问题
//fib(n) 就能得到第n位的数字
//fib(2) = 1
//fib(5) = 5
//fib(6) = 8
//fib(10) = 55
function fib(n){
if(n == 1 || n == 2){
return 1;
}else{
return fib(n - 1) + fib(n - 2);
}
}
//一点一点计算:
for(var i = 1 ; i <= 55 ;i++){
console.log(fib(i));
}
二. 函数表达式
定义函数除了使用function之外,还有一种方法,就是函数表达式。就是函数没有名字,称为“匿名函数”,为了今后能够调用它,我们把这个匿名函数,直接赋值给一个变量。
var sum = function(a,b){
return a + b;
}
以后想调用这个函数的时候,就可以直接使用sum变量来调用。
console.log(sum(3,6));
如果现在这个函数表达式中的function不是匿名的,而是有名字的:
var sum = function fun(a,b){
return a + b;
}
那么JS表现非常的奇怪,在外部只能用sum()
来调用,fun()
会引发错误!
也就是说,JS这个奇怪的特性,给我们提了个醒,定义函数,只能用这两种方法中的其一,不建议杂糅:
第一种,通过函数声明定义函数
function sum(){
}
第二种,通过匿名函数的赋值定义函数
var sum = function(){
}
建议不要混用,这么写也不算错
var xixi = function haha(){
}
三. 函数声明的提升
JS在执行前,会有一个预解析的过程,把所有的函数声明,都提升到了最最开头,然后再执行第一行语句。
所以,function定义在哪里,都不重要,程序总能找到这个函数。
//先调用
fun();
//然后定义
function fun(){
console.log("我是函数,我执行了!");
}
不会引发错误,打印能够正常执行。
1. 声明函数和函数表达式提升方式不同
函数声明会被提升,但是函数表达式却不会被提升
函数表达式提升的是变量,变量提升后并不是一个函数,所以在表达式之前执行,会报错,为类型错误,因为不是函数.
fun(); //报错
var fun = function(){
alert("我是函数,我执行了!");
}
这提了个醒,没有极特殊的理由,都要使用function 关键字来定义函数,而不要使用函数表达式来定义函数.
1.1.函数优先
sum(); //现在这个sum到底是函数,还是变量8呢?
//函数优先,遇见同名标识符,预解析阶段一定把这个标识符给函数
var sum = 8; //定义一个变量,是8
function sum(){
alert("我是sum函数,我执行了");
}
面试很容易考,就常见的面试题:
foo();
var foo;
function foo(){
console.log(1);
}
foo = function(){
console.log(2);
}
函数优先,
现在foo这个标识符冲突了,一个函数叫做foo,一个变量也叫作foo。预解析阶段,如果遇见标识符冲突,这个标识符给函数。
四.IIFE
IIFE就是immediately-invoked function expression,即时调用函数表达式
如果一个函数,在定义的时候,我们就想直接调用它,就是一个IIFE。
我们试图在定义函数的后面,直接写圆括号:
function fun(){
alert("你好啊")
}();
控制台报错,这是因为函数是一个函数体,并不是表达式,只有表达式能够用()来执行。
所以就要把function fun(){}“降级”, 从函数体降级为表达式。方法有很多:
+function fun(){
alert("你好啊")
}();
-function fun(){
alert("你好啊")
}();
更通常更常用的:
(function fun(){
alert("你好啊")
})();
用这种方法定义的函数,名字是无效的,其他的地方是调用这个函数
fun();
所以IIFE里面的函数,都是匿名函数:
(function(){
alert("哈哈");
})();
上面就是一个标准的IIFE。