04.函数

一.初步了解函数

1.函数的定义和调用

1.1 函数定义

函数其实就是将多条语句组合成一个“语句军团”,集体作战

//定义一个函数,函数就是一组语句的集合
function fun(){
    console.log(1);
    console.log(2);
    console.log(3);
    console.log(4);
}

//函数调用
fun(); 

\color{red}{函数必须先定义然后才能调用}
定义一个函数,用关键字function来定义
function后面有一个空格,后面就是函数名字,函数的 名字也是标识符,既然是标识符,那么命名规范和变量命名就是一样的。

定义一个函数也叫声明一个函数

function 函数名(){

}

函数如果不调用,那么里面的语句就不会执行,不调用就等于白写。

1.2 函数调用

调用一i个函数的方法非常简单,函数名后面加一个(),
( ) 是运算符, 表示执行一个函数

执行函数:

函数名()

一旦调用了函数,函数内部的语句就会执行

2.函数的参数

2.1 参数的了解

可以通过参数让函数内部的语句有所不同
定义函数的时候,内部语句可能有一些悬而未决的量,就是变量,这些变量,我们要求在定义的时候罗列在小括号中:
比如:

function fun(num){
      console.log("这已经是我第” +  num + "此说我爱你!");
}

调用的时候,要把这个变量的真实的值,一起写在括号里,这样随着函数的调用,这个值也传给了num';这就是参数传递
执行这个函数:

fun(99);

罗列在function小括号中的参数,叫做\color{red}{形式参数}
调用时传递的值,叫做\color{red}{实际参数}
形式参数就像占位置,先把位置站好,等你来赋值

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,30,40,50);    //30

只有前两个参数被形参接受了,后面的参数无视了,
形参会按顺序接收实参,

2.4 arguments

每一个函数里面都有一个隐式的arguments,这个时系统已经给你创建好的,每个函数内部都有,arguments是类似于数组的类数组,就是实参列表
类数组本质上是一个对象,涵盖了所有的实参

调用函数的时候,比如:

fun(45,56,346,335,231);

此时函数内部,arguments就有一个下标,就依此等于上面调用的数:

arguments[0]     //45
arguments[1]     //56
arguments[2]     //346
arguments[3]     //335
arguments[4]     //231
.....

如果函数里面有形式参数列表,那么是和arguments同步的:

 function fun(a,b){
         arguments[0] = 8;//改变第一个参数值
         alert(a);   // 8 ,弹出改变后的值

        console.log( arguments.length); //实参的长度
        console.log( fun.length); //形参的 长度 

fun(45,56,346,335,231);

arguments的功能是模拟函数的重载,使得同一个函数,根据参数个数额不同,有不同的作用

4. 返回值

函数可以通过参数来接收东西,可以通过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

程序是先执行内层在执行外层。
函数可以接收很多值,返回一个值

函数的意义:

  1. 在出现大量程序相同的时候,可以封装为一个function,这样只要写一次代码就可以调用很多次,减少代码耦合
  2. 在调用一个函数的时候,不用关心函数内部的实现细节,只要可以运用,并能给我们开发带来好处
  3. 模块化编程,然复杂的逻辑变得简单

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; <= 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("我是函数,我执行了");
}

不会引发错误,打印能正常执行。

3.1. 声明函数和函数表达式提升方式不同

函数声明会被提升,但函数表达式却不会提升
函数表达式提升的是变量,变量提升后并不是一个函数,所以在表达式之前执行,会报错,为类型错误,因为不是函数

fun() ;//报错
var fun = function(){
      alert("我是函数,我执行了");
}

这提了个醒,没有极特殊的理由,都要使用function 关键字来定义函数,而不要使用函数表达式来定义函数.

3.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("hello ");
}();

控制台报错,这是因为函数是一个函数体,并不是表达式,只有表达式能够用()来执行。
所以就要把function fun(){}“降级”,从函数体降级为表达式。

+function fun(){
    alert("hello");
 }();
-function fun(){
    alert("hello");
 }();

更通常更常用的:

(function fun(){
      alert("hello");
})(); 

用这种方法定义的函数,名字是无效的,其他的地方是调用这个函数:

fun();

所以IIFE里面的函数,都是匿名函数:

(function(){
  alert("哈哈");
})();

上面就是一个标准的IIFE.

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容