今天来复习js中函数的相关知识,主要包括函数的概念以及相关用法。
函数概念
- 函数(function),也叫做功能、方法,函数可以将一段代码封装起来,被封装的函数具备某一项特殊的功能,内部封装的一段代码作为一个完整的结构体,要执行就都执行,要不执行就都不执行
- 函数的作用就是封装一段代码,将来可以重复使用
函数声明
- 函数声明又叫函数定义,函数必须先定义再使用
- 如果没有定义函数直接引用,会报一个引用错误
- 函数声明语法:
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类数组对象中
作用域
- 作用域:变量可以起作用的范围
- 如果变量定义在一个函数的内部,只能在函数内部被访问到,在函数外部是访问不到这个变量的,函数就是变量定义的作用域
- 任何一对花括号{}中的结构体都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域
- 在es5之前没有块级作用域的概念,只有函数作用域
全局变量和局部变量
- 局部变量:定义在函数内部的变量,只能在函数作用域内部被访问到,在外面没有定义的
- 全局变量:从广义上说,它也是一种局部变量,定义在全局的变量,作用域范围是全局,在整个js程序任意位置都能够被访问到
- 变量退出作用域之后会销毁,全局变量关闭网页或浏览器才会销毁
作用域链
- 只有函数可以制造作用域结构,那么只要是代码,就至少有一个作用域,即全局作用域。凡是代码里有函数,那么这个函数就构成另外一个作用域。如果函数中还有函数,那么在这个作用域中又有一个作用域
- 将这样的所有的作用域列出来,可以有一个结构:函数内部指向函数外部的链式结构,称为作用域链
遮蔽效应
- 程序在遇到一个变量时,使用作用域查找顺序,不同层次的函数内可能有相同名字的变量,一个变量在使用时,会优先从自己所在层作用域查找变量,如果当前层没有变量定义会按照顺序从本层依次往外寻找直到找到第一个变量定义。整个过程会发生内层变量遮蔽外层变量的效果,这个效果称为遮蔽效应
不写var关键字的影响
- 在函数内部想要定义新的变量,如果不加关键字var,相当于定义了一个全局变量。如果全局也有相同的标识符,会被函数内部的变量影响,局部变量污染全局变量
- 注意:每次定义变量时都必须写var关键字,否则就会定义在全局,可能污染全局
预解析
- JavaScript代码的执行是由浏览器中的JavaScript解析器来执行的。JavaScript解析器执行JavaScript代码的时候,分为两个过程:预解析过程和代码执行过程
- 预解析过程:
1.把变量的声明提升到当前作用域的最前面,只会提升声明,不会提升赋值
2.把函数的声明提升到当前作用域的最前面,只会提升声明,不会提升调用
3.先提升var,再提升function - JavaScript的执行过程:在预解析后,根据新的代码顺序,从上往下按照既定规律执行js代码
变量声明提升
- 在预解析过程中,所有定义的变量,都会将声明的过程提升到所在的作用域最上面,在将来的代码执行过程中,按照先后顺序会先执行被提升的的声明变量过程
- 提升过程中,只提升声明过程,不提升变量赋值,相当于变量定义未赋值,变量内存储undefined值
- 所以,在js中会出现这样一种情况,在前面调用后面定义的变量,不会报错,只会使用undefined值
函数声明提升
- 在预解析过程中,所有定义的函数,都会将声明的过程提升到所在的作用域的最前面,在将来的代码执行过程中,按照先后顺序会先执行被提升的函数声明过程
- 在预解析之后的代码执行过程中,函数定义过程已经在最开始就会执行,一旦函数定义成功后,后续就可以直接调用函数
- 所以,在js中会出现这样一种情况,在前面调用后面定义的函数,不会报错,而且能正常执行函数内部的代码
提升顺序
- 预解析过程中,先提升var变量声明,再提升function函数声明
- 假设出现变量名和函数名相同,那么后提升的函数名标识符会覆盖先提升的变量名,那么在后续代码中出现调用标识符时,内部是函数的定义过程,而不是undefined
- 建议:不要书写相同的标识符给变量名或函数名,避免覆盖
函数表达式提升
- 在预解析过程中,函数表达式进行的是变量声明提升,而不是函数声明提升。提升后变量内部存储的是undefined。在前面进行函数方法调用,数据类型会显示错误
- 建议:定义函数时,最好使用function关键字定义,这样函数声明提升可以永远生效
IIFE自调用函数
- IIFE:即时调用的函数表达式,也叫做自调用函数,表示函数在定义时就立即调用
- 函数调用方式:函数名或者函数表达式的变量名后面加()运算符
- 函数名定义的形式不能实现立即执行自调用,函数使用函数表达式形式可以实现立即执行,原因是因为函数表达式定义过程中,将一个函数矮化成了表达式,后面加()运算符就可以立即执行
- 如果想实现IIFE,可以想办法将函数矮化成表达式
函数矮化成表达式的方法
- 给函数前面加一些运算符,比如数学运算符+、-、(),或者逻辑运算符!非运算
- IIFE结构可以关住函数的作用域,在结构外面是不能调用函数的
- IIFE最常用的是()运算符,而且函数可以不写函数名,使用匿名函数:比如
(function(a){ console.log(a);})(1);
ok,函数的部分复习完了,今日份推荐歌曲:黄安-新鸳鸯蝴蝶梦