整体介绍

函数基本使用 1)什么是函数 2)函数的定义和调用 3)函数的参数和返回值 函数算法题 1)函数算法题1 2)函数算法题2 递归 1)什么是递归 2)递归常见算法题 3)实现深克隆 作用域和闭包 1)全局变量和局部变量 2)闭包 立即执行函数 1)立即执行函数IIFE

什么是函数

函数就是语句的封装,可以让这些代码方便地被复用 函数具有”一次定义,多次调用”的优点 使用函数,可以简化代码,让代码更具有可读性

函数的定义和调用

函数的定义

和变量类似,函数必须先定义然后才能使用 使用function关键字定义函数,function是”功能”的意思

函数表达式

函数的调用

◆执行函数体中的所有语句,就称为“调用函数” ◆调用函数非常简单,只需在函数名字后书写圆括号对即可 fun() //调用函数

<body>
  <script>
    //定义函数,定义函数的是不会直接执行的
    function fun(){
      console.log('你好');
      console.log('今天天气真好');
    }
    //函数必须要等到调用的时候才能被执行
    fun();
  </script>
</body>

语句执行顺序

函数声明的提升

和变量声明提升类似,函数声明也可以被提升

函数表达式不能提升

如果函数是用函数表达式的写法定义的,则没有提升特性

本质上定义变量,只不过把函数的值赋予变量,变量的提升只提升定义,不提升值,值为undefined,undefined不能加以圆括号运行

函数优先提升

程序执行前会通看所有函数,预解析函数,再解析变量,函数表达式因为无法提升,所以不能覆盖函数优先提升的,所以先弹出B,然后var fun里的fun已经存储了A,覆盖了B,所以下一个function函数不会执行(fun有值了),这个fun在预解析阶段定义的,不会再覆盖回来,所以下一个弹出A

函数的参数和返回值

函数的参数

参数是函数内的一些待定值,在调用函数时,必须传入这些参数的具体值 函数的参数可多可少,函数可以没有参数,也可以有多个参数,多个参数之间需要用逗号隔开

形参和实参个数不同的情况

arguments

函数内arguments表示它接收到的实参列表,它是一个类数组对象

类数组对象:所有属性均为从0开始的自然数序列,并且有length属性,和数组类似可以用方括号书写下标访问对象的某个属性值,但是不能调用数组的方法

<body>
  <script>
    function fun(){
      console.log(arguments);//输出Arguments(4)[33,44,23,34,caLLee:f,SymboL(SymboL.iterator):f]
      console.log(arguments[0]);//输出33
      console.log(arguments[1]);//输出44
    fun(33,44,23,34);
  </script>
</body>

用途:不管用户传入多少个实际参数,永远能够计算它们的和,而且有length属性

函数的返回值

函数体内可以使用return关键字表示”函数的返回值”

调用一个有返回值的函数,可以被当做一个普通值,从而可以出现在任何可以书写值的地方

遇见return.即退出函数

调用函数时,一旦遇见return语句则会立即退出函数,将执行权交还给调用者

C不会输出 结合if语句的时候,往往不需要写else分支了

什么是递归

函数的内部语句可以调用这个函数自身,从而发起对函数的一次迭代。在新的迭代中,又会执行调用函数自身的语句,从而又产生一次迭代。当函数执行到某一次时,不再进行新的迭代,函数被一层一层返回,函数被递归。

递归要素

边界条件:确定递归到何时终止,也称为递归出口 递归模式:大问题是如何分解为小问题的,也称为递归体

实现深克隆

◆JavaScript中的数据类型有两类:基本类型和引用类型值

复习浅克隆

使用var arr2=arr1这样的语句不能实现数组的克隆 浅克隆:准备一个空的结果数组,然后使用fo循环遍历原数组,将遍历到的项都推入结果数组

浅克隆只克隆数组的一层,如果数组是多维数组,则克隆的项会“藕断丝连”

实现深克隆

使用递归思想,整体思路和浅克隆类似,但稍微进行一些改动:如果遍历到项是基本类型值,则直接推入结果数组;如果遍历到的项是又是数组,则重复执行浅克隆的操作。

<body>
  <script>
    //原数组
    var arr1=[33,44,11,22,[77,88,[33,44]];
    //函数,这个函数会被递归
    function deepclone(arr){
       //结果数组
       var result =[];
      //遍历数组的每一项
       for (var i = 0;i<arr.length;i++){
        //类型判断,如果遍历到的项是数组
         if (Array.isArray(arr[i])){
          //递归
          result.push(deepclone(arr[i]));
          }else{
             //如果遍历到的项不是数组,是基本类型值,就直接推入到结果数组中,
             //相当于是递归的出口
             result.push(arr[i]);
        }
      }
      //返回结果数组
      return result;
    }
     //测试一下
     var arr2  = deepclone(arr1);
     console.log(arr2);
 
     //是否藕断丝连
     console.log(arr1[4]==arr2[4]);
     arr1[4].push(99);
     console.log(arr2);
  </script>
</body>

全局变量和局部变量

变量作用域

◆JavaScript是函数级作用域编程语言:变量只在其定义时所在的function内部有意义。

全局变量

如果不将变量定义在任何函数的内部,此时这个变量就是全局变量,它在任何函数内都可以被访问和更改。

遮蔽效应

如果函数中也定义了和全局同名的变量,则函数内的变量会将全局的变量“遮蔽”

注意考虑变量声明提升的情况

局部变量

形参也是局部变量

作用域链

先来认识函数的嵌套:一个函数内部也可以定义一个函数。 和局部变量类似,定义在一个函数内部的函数是局部函数。

在函数嵌套中,变量会从内到外逐层寻找它的定义。

不加var将定义全局变量

在初次给变量赋值时,如果没有加var,则将定义全局变量

var a = 3;
var b = 2;
function fun(){
    a=3;
    a++;
    var b = 4;
}
fun();
console.log(a);  //4
console.log(b);  //2

闭包

结果弹出MK网三个字

什么是闭包

◆JavaScript中函数会产生闭包(closure)。闭包是函数本身和该函数声明时所处的环境状态的组合。

函数能够“记忆住”其定义时所处的环境,即使函数不在其定义的环境中被调用,也能访问定义时所处环境的变量。

观察闭包现象

◆在JavaScript中,每次创建函数时都会创建闭包。 但是,闭包特性往往需要将函数“换一个地方”执行,才能被观察出来。

闭包非常实用

闭包很有用,因为它允许我们将数据与操作该数据的函数关联起来。这与“面向对象编程”有少许相似之处。

闭包的功能:记忆性、模拟私有变量

闭包用途1-记忆性

当闭包产生时,函数所处环境的状态会始终保特在内存中,不会在外层函数调用后被自动清除。这就是闭包的记忆性。

创建体温检测函数checkTemp(n),可以检查体温n是否正常,函数会返回布尔值。

但是,不同的小区有不同的体温检测标准,比如A小区体温合格线是37.1℃,而B小区体温合格线是37.3℃,应该怎么编程呢?

<body>
    <script>
        function createCheckTemp(standardTemp){
                function checkTemp(n){
                    if (n <standardTemp){
                        alert('你的体温正常');
                    }else{
                        alert('你的体温偏高');
                    }
                }
                return checkTemp;
            }
        var checkTemp_A  = createCheckTemp(37.1);
        checkTemp_A(37.2);
        checkTemp_A(37.0);
    </script>
</body>

类似面向对象中初始化对象 这个闭包就是checkTemp所处函数体和standaardTemp形参,所以会一直记住standardTemp

闭包用途2-模拟私有变量

题目:请定义一个变量a,要求是能保证这个a只能被进行指定操作(如加1、乘2),而不能进行其他操作,应该怎么编程? 程呢?

在Java、C++等语言中,有私有属性的概念,但是JavaScript中只能用闭包来模拟。

<body>
  <script>
    //封装一个函数,这个函数的功能就是私有化变量
    function fun(){
      //定义一个局部变量a
      var a =0;
      return{
        getA:function(){
          return a;
      },
         add:function()
           a++;
     },
         pow:function ()
           a*=2;
     }
   };
}
var obj = fun();
//如果想在fun函数外面使用变量a,唯一的方法就是调用getA()方法
console.log(obj.getA());
obj.add();
obj.pow();
console.log(obj.getA());

类似于C++面向对象的封装。

使用闭包的注意点

不能滥用闭包,否则会造成网页的性能问题,严重时可能导致内存泄露。所谓内存泄漏是指程序中己动态分配的内存由于某种原因未释放或无法释放。

答案:1 1 2 2

立即执行函数IIFE

什么是IIFE

◆IIFE (Immediately Invoked Function Expression,调用函数表达式)是一种特殊的JavaScripti函数写法,一旦被定义,就立即被调用。

形成IIFE的方法

IIFE的作用1-为变量赋值

为变量赋值:当给变量赋值需要一些较为复杂的计算时(如if语句),使用IIFE显得语法更紧凑。

IIFE的作用2-将全局变量变为局部变量

循环结束时i= 5,覆盖了所有值,退出循环,结果为5

IIFE可以在一些场合(如for循环中)将全局变量变为局部变量,语法显得紧凑。

末尾的(i)传给了function后面的(i)作为形参,全局变量i变成局部变量i,形参i与函数体形成闭包