JavaScript函数

函数的概念

  • 函数也叫作功能,方法,函数可以把一段代码封装,被封装的函数具有一项特殊的功能,内部封装的一段代码作为一个完整的结构体,要执行就都执行,不执行就都不执行
  • 作用:封装一段代码,将来可以重复使用

函数声明

  • 又叫做函数定义,函数需要先定义才能使用
  • 没有定义函数直接使用,会出现一个引用错误
  • 声明语法:
function 函数名(参数){
      封装的结构体;
}

函数声明的时候,不会执行,调用的时候才会执行

函数调用

  • 调用方法:函数名();
  • 函数调用也叫作函数执行,调用时会将函数内部封装的所有的结构体的代码立即执行。
  • 函数内部语句执行的位置,与函数定义的位置无关,与函数调用位置有关。
  • 函数可以一次定义,多次执行

函数的参数1

  • 执行结果不能是一成不变的,所以根据自定义的内容发生一些变化
  • 函数预留了一个接口,专门用于让用户自定义内容,使函数发生一些执行效果变化
  • 接口:就是函数的参数,函数参数的本质就是变量,可以接受任意类型的数据,导致函数执行结果根据参数的不同,结果也不同
  • 可以设置0个或者多个参数,参数之间用逗号分隔开来

函数的参数2

  • 参数根据书写位置的不同,名称也不同
  • 形式参数:定义的()内部的参数,叫做形式参数,本质是变量,可以接收实际参数传递过来的数据,简称形参
  • 实际参数:调用的()内部的参数,叫做实际参数,本质就是传递的各种类型的数据,传递给每个形参,简称实参
  • 函数执行过程,伴随着传参的过程


    实参和形参的关系

函数的参数的优点

  • 不论使用自己封装的函数,还是其他人封装的函数,只需要知道传递什么参数,执行什么功能,没必要知道内部的结构是什么
  • 一般自己封装的函数或者其他人封装的函数需要有一个API接口说明,告诉用户参数需要传递什么类型的数据,实现什么功能

函数的返回值

  • 能够通过参数接收数据,也能够将函数执行结果返回一个值
  • 利用函数内部的一个return的关键字设置函数的返回值
  • 作用1:函数内部如果结构体执行到了一个return的关键字,会立即停止后面代码的执行
  • 作用2:可以在return关键字后面添加空格,空格后面任意定义一个数据字面量或者表达式,函数在执行完自身功能之后,整体会被return矮化成一个表达式,表达式必须求出一个值继续可以参与程序,表达式的值就是return后面的数据

函数的返回值的应用

  • 如果函数有返回值,执行结果可以当成普通数据参与程序
  • 函数有返回值,可以作为一个普通数据赋值给一个变量,甚至赋值给其他函数的实际参数
  • 注意:如果函数没有设置return语句,那么函数有默认的返回值undefined;如果函数有使用return语句,但是return后面没有任何值,那么函数的返回值也是undefined

函数表达式

  • 函数表达式就是函数定义的另外一种方式
  • 定义方法:将函数的定义、匿名函数赋值给一个变量
  • 函数定义赋值给一个变量,相当于将函数整体矮化成了一个表达式
  • 匿名函数:函数没有函数名
  • 调用函数表达式,方法是给变量名加()执行,不能使用函数名加()执行

函数的数据类型

  • 函数是一种单独的数据类型Function
  • 由于函数是一种数据类型,可以参与其他程序
  • 例如,可以将函数作为另一个函数的参数,在另一个函数中调用
  • 或者,可以把函数作为返回值从函数内部返回

arguments 对象

  • JavaScript 中,arguments 对象是比较特别的一个对象,实际上是当前函数的一个内置属性。也就是说所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有的实参。arguments 是一个伪数组,因此及可以进行遍历。
  • 因此函数的实参个数和形参个数可以不一致,所有的实参都会存储在函数内部的arguments类数组对象中
function fun(){
    console.log(arguments);
}
fun(1,2,3,4,5,6,7);

如上述代码,可以判断用户到底输入了几个实参,也可以查出实参长度,也可以使用遍历方法获取每一个实参

      // 使用数组的遍历方法可以获取每一项实参
      for (var i = 0 ; i <= arguments.length - 1 ; i++) {
        console.log(arguments[i]);
      }
    }
    // 调用函数
    fun(1,2,3,4,5,6,7);

案例: 定义一个求和函数,如果传入 1 个参数,返回它自己,如果传入两个参数,返回他们的和,如果传入三个参数,先比较前两个的大小,大的与第三个参数求和返回,如果传入 4 个及以上,输出错误提示。

 function sum(a,b,c) {
      // 条件分支语句,根据实参个数走不同的分支
      switch (arguments.length) {
        case 1:
          return a;
          break;
        case 2:
          return a + b;
          break;
        case 3:
          return a > b ? a + c : b + c ;
          break;
        default:
          // 提示用户,实参个数传递错误
          // 模拟控制台报错
          throw new Error("参数个数不能超过 3 个");
      }
    }
    // 调用函数
    console.log(sum(1));
    console.log(sum(1,2));
    console.log(sum(1,2,3));
    console.log(sum(1,2,3,4,5));

函数递归

  • 函数内部可以通过函数名调用函数自身的方式,就是函数递归现象
  • 递归的次数太多容易出现错误,超出计算机的计算最大能力
  • 更多时候,使用递归去解决一些数学中的现象
  • 例如斐波那契数列
var a = prompt("输入您想到的数字");
   function feibo(a){
     if(a==1 || a==2){
       return 1;
     }else {
       return feibo(a-1) + feibo(a-2);
     }
   }
   console.log(feibo(a));

作用域

  • 作用域:变量可以起作用的范围。
  • 如果变量定义在一个函数内部,只能在函数内部被访问到,在函数外部不能使用这个变量,函数就是变量定义的作用域。
  • 任何一对花括号 {} 中的结构体都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域。
  • 在es5之前没有块级作用域的的概念,只有函数作用域,现阶段可以认为 JavaScript 没有块级作用域.

全局变量和局部变量

  • 局部变量:定义在函数内部的变量,只能在函数作用域内部被访问到,在外面没有定义的。
  • 全局变量:从广义上来说,也是一种局部变量,定义在全局的变量,作用域范围是全局,在整个 js 程序任意位置都能够被访问到。
  • 变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁。

函数的参数也是局部变量

函数的参数本质是一个变量,有自己的作用域,函数的参数也是属于函数自己内部的局部变量,只能在函数内部被使用,在函数外面没有定义

函数自己的作用域

如果定义函数是定义在另一函数的内容,那么调用的时候也应该在其内部进行调用

function outer() {
      var a = 1;
      function inner() {
        console.log(2);
      }
      // 函数内部调用子函数才能成功
      inner();
    }
    // 调用函数
    outer();
    // inner();

作用域链

  • 只有函数可以制造作用域结构,只要是代码,就至少有一个作用域,也就是所谓的全局作用域,凡是代码中有函数,那么函数就构成另一个作用域,如果函数中还有函数,那么这个函数的作用域里还可以诞生另一个作用域
  • 将这样的所有的作用域列出来,可以有一个结构:函数指向函数外的链式结构。我们就称之为作用域链
 // 全局作用域
    var a = 1;
    // 创建函数
    function outer() {
      var a = 2;
      // 内部函数
      function inner() {
        var a = 3;
        console.log(a);
      }
      inner();
      console.log(a);
    }
    // 调用
    outer();
    console.log(a);

第一个console.log(a)是处于inner的作用域内,所以根据逻辑应该打印的数据为3,相应的,outer的作用域内为2,全局变量外打印应该为1


遮蔽效应

程序在遇到一个变量时,使用时作用域查找顺序,不同层次的函数内都有可能定义相同名字的变量,一个变量在使用时,会优先从自己所在层作用域查找变量,如果当前层没有变量定义会按照顺序从本层往外依次查找,直到找到第一个变量定义。整个过程中会发生内层变量遮蔽外层变量的效果,叫做“遮蔽效应”。


如果把inner函数内部的变量a声明注释掉

效果,找不到变量a就往外查询,以此类推

不写 var 关键字的影响

  • 不写var关键字的变量也可以调用
  • 在函数内部想要定义新的变量,如果不加var关键字,相当于定义的全局变量,如果全局也有相同的标识符,会被函数内部的变量影响到,局部变量污染全局变量
// 全局作用域
    // 没有定义全局变量但是outer函数内部没有声明var的定义了一个a变量也就相当于定义了全局变量a
    // var a;
    // 创建函数
    function outer() {
      a = 2;
      // 内部函数
      function inner() {
        var a = 3;
        console.log(a);
      }
      inner();
      console.log(a);
    }
    // 调用
    outer();
    console.log(a);
  • 每次定义变量的时候都必须要写var关键字,否则就会定义在全局,可能污染全局

预解析与声明提升

  • JavaScript 代码的执行是由浏览器中的 JavaScript 解析器来执行的。JavaScript 解析器执行JavaScript 代码的时候,分为两个过程:预解析过程和代码执行过程。
  • 预解析过程:
    1. 把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值。
    2. 把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用。
    3. 先提升 var,再提升 function。
  • JavaScript 的执行过程:在预解析之后,根据新的代码顺序,从上往下按照既定规律执行 js 代码。

变量声明提升

  • 在预解析过程中,所有定义的变量,都会将声明的过程提升到所在的作用域最上面,在将来的代码执行过程中,按照先后顺序会先执行被提升的声明变量过程。
  • 提升过程中,只提升声明过程,不提升变量赋值,相当于变量定义未赋值,变量内存储undefined 值。
  • 因此,在 js 中会出现一种现象,在前面调用后定义的变量,不会报错,只会使用undefined 值

函数声明提升

  • 在预解析过程中,所有定义的函数,都会将声明的过程提升到所在的作用域最上面,在将来的代码执行过程中,按照先后顺序会先执行被提升的函数声明过程。
  • 在预解析之后的代码执行过程中,函数定义过程已经在最开始就会执行,一旦函数定义成功,后续就可以直接调用函数。
  • 因此,在 js 中会出现一种现象,在前面调用后定义的函数,不会报错,而且能正常执行函数内部的代码。
   fun();
    // 定义函数
    function fun() {
      console.log(2);
    }
    // 调用
    fun();
//两次调用都可以实现

提升顺序

  • 预解析过程中,先提升 var 变量声明,再提升 function 函数声明。
  • 假设出现变量名和函数名相同,那么后提升的函数名标识符会覆盖先提升的变量名,那么在后续代码中出现调用标识符时,内部是函数的定义过程,而不是 undefined。
  • 如果调用标识符的过程在源代码函数和变量定义后面,相当于函数名覆盖了一次变量名,结果在执行到变量赋值时,又被新值覆盖了函数的值,那么在后面再次调用标识符,用的就是变量存的新值。
  • 建议:不要书写相同的标识符给变量名或函数名,避免出现覆盖。

函数表达式的提升

  • 在预解析过程中,函数表达式进行的是变量声明提升,而不是函数声明提升,提升后变量内部的是一个undefined。在前面进行函数方法调用,数据类型会提示错误
  • 建议:定义函数的时候,最好使用function关键字定义方式,函数声明提升就可以永久生效了

函数声明提升的应用

  • 函数声明提升可以用于调整代码的顺序,将大段的定义过程放到代码最后,但是不影响代码执行效果。

IIFE自调用函数

  • IIFE:叫做即时调用的函数表达式,也叫做自调用函数,表示函数在定义时就立即调用
  • 调用方式:函数名或者函数表达式的变量名后面加()运算符
  • 函数名定义的形式不能实现立即执行自调用,函数使用函数表达式可以实现立即执行,原因是因为函数表达式定义过程中,将一个函数矮化成了一个表达式,后面加()运算符就可以立即执行
   // 关键字定义的方式,不能立即执行
    function fun() {
      console.log(1);
    }();

    // 函数表达式方式,可以在定义时被立即执行
    var foo = function fun() {
      console.log(2);
    }();

如果想实现 IIFE,可以想办法将函数矮化成表达式。

  • 函数矮化成表达式,就可以实现自调用。
  • 函数矮化成表达式的方法,可以让函数参与一些运算,也就是说给函数前面加一些运算符。
    1.数学运算符:+ - ()
    2.逻辑运算符:!非运算
  • IIFE 结构可以关住函数的作用域,在结构外面是不能调用函数的
  • IIFE 最常用的是 () 运算符,而且函数可以不写函数名,使用匿名函数。
 (function (a) {
      console.log(a);
    })(4);
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • 函数的定义和调用 arguments arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的...
    子尐小太爷阅读 391评论 0 2
  • 函数就是最基本的一种代码抽象的方式。 定义函数function abs(x) {if (x >=0){return...
    _我和你一样阅读 445评论 0 0
  • 函数函数定义与调用变量作用域全局变量方法高阶函数闭包箭头函数$generator$ 函数 函数定义与调用 定义函数...
    染微言阅读 583评论 0 5
  • 本文是大神廖雪峰的JavaScript教程学习笔记。并不是教程,如有需要,请前往廖雪峰大神大博客. 一、函数定义和...
    0o冻僵的企鹅o0阅读 486评论 1 3
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,519评论 16 22