JavaScript 之 函数
创建函数
函数结构需要符合一定的语法规范,当前创建函数有三种方式:
- 函数声明方式。
- 函数表达式方式。
- 利用Function构造函数。
函数声明方式
语法规则:
1 |
|
- 函数声明不是一个可以执行的语句,所以不以分号结束。
- 函数声明方式创建函数,函数名称是必须的,表达式方式则是可选的
- 函数名称后面小括号是必须的。
- 参数是可选的。
- 大括号中内容可以为空,但是大括号不能省略。
示例如下:
1 |
|
表达式方式声明
表达式方式声明的本质是:函数可以通过一个表达式定义,然后储存在变量中
语法规则:
1 |
|
- 表达式方式声明的函数,名称是可选的,这一点与函数声明方式不同。
- 以分号结尾,因为这是一个可执行的语句。
代码示例:
1 |
|
上述代码就是一个赋值表达式,右侧函数带有名字,最常见的是没有名字(匿名)的形式:
1 |
|
自执行函数
!function(形参列表){}(实参列表)
, 可以使用多种运算开头, 但一般用感叹号!
。
1 |
|
(function(形参列表){}(实参列表))
, 使用()
将函数及函数后设的小括号包裹起来。
1 |
|
(function(){})()
, 使用()
值包裹函数部分。
1 |
|
三种写法的特点:
- 使用
!
开头, 结构清晰, 不容易混乱, 推荐使用; - 能够表明匿名函数与调用的
()
为一个整体, 官方推荐使用; - 无法表明函数与之后的
()
的整体性, 不推荐使用;
箭头函数
ES2015(_ES6)新增箭头函数,全部是匿名函数,也就是通过表达式方式声明。
1 |
|
如果箭头函数不需要参数或需要多个参数,就是用圆括号代表参数部分:
1 |
|
Function构造函数方式
function函数自身也是一个对象实例,它可以由内置的JavaScript函数构造器Function构造函数创建。
1 |
|
也可以写成
1 |
|
函数的调用与返回值
函数名称+()可以调用指定函数,通过小括号可以传递参数。对于任何方式声明的函数都适用,代码实例如下:
1 |
|
通过语句可以返回一个值,如果没有return语句,相当于返回undefined。
函数的参数和关键字
arguments关键字
arguments用于储存调用函数时的所有实参, arguments是一个数组, 索引从 0 开始, 表示实参的个数, arguments.callee() 调用函数本身,arguments.length 属性返回函数调用过程接受到的参数个数
arguments关键字:只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。利用arguments
,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值
1 |
|
rest参数
由于JavaScript函数允许接收任意个参数,于是我们就不得不用
arguments
来获取所有参数:ES6标准引入了rest参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function foo(a, b, ...rest) {
console.log('a = ' + a);
console.log('b = ' + b);
console.log(rest);
}
foo(1, 2, 3, 4, 5);
// 结果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]
foo(1);
// 结果:
// a = 1
// b = undefined
// Array []
rest参数只能写在最后,前面用...
标识,从运行结果可知,传入的参数先绑定a
、b
,多余的参数以数组形式交给变量rest
,所以,不再需要arguments
我们就获取了全部参数。
如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined
)。
函数返回值
函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。
函数的全局变量和局部变量(理解即可)
局部变量: 在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。
全局变量: 在函数外声明的变量是_全局_变量,网页上的所有脚本和函数都能访问它。
变量生存周期:
- JavaScript变量的生命期从它们被声明的时间开始。
- 局部变量会在函数运行以后被删除。
- 全局变量会在页面关闭后被删除。
作用域
首先在函数内部查找变量,找不到则到外层函数查找,逐步找到最外层。
几个例子:
1 |
|
词法分析(尝试理解)
JavaScript中在调用函数的那一瞬间,会先进行词法分析。
词法分析的过程:
当函数调用的前一瞬间,会先形成一个激活对象:Avtive Object(AO),并会分析以下3个方面:
- 函数参数,如果有,则将此参数赋值给AO,且值为undefined。如果没有,则不做任何操作。
- 函数局部变量,如果AO上有同名的值,则不做任何操作。如果没有,则将此变量赋值给AO,并且值为undefined。
- 函数声明,如果AO上有,则会将AO上的对象覆盖。如果没有,则不做任何操作。
函数内部无论是使用参数还是使用局部变量都到AO上找。看两个例子:
1 |
|
词法分析过程:
1、分析参数,有一个参数,形成一个 AO.age=undefine;
2、分析变量声明,有一个 var age, 发现 AO 上面已经有一个 AO.age,因此不做任何处理
3、分析函数声明,有一个 function age(){…} 声明, 则把原有的 age 覆盖成 AO.age=function(){…};
最终,AO上的属性只有一个age,并且值为一个函数声明
执行过程:
注意:执行过程中所有的值都是从AO对象上去寻找
1、执行第一个 console.log(age) 时,此时的 AO.age 是一个函数,所以第一个输出的一个函数
2、这句 var age=22; 是对 AO.age 的属性赋值, 此时AO.age=22 ,所以在第二个输出的是 22
3、同理第三个输出的还是22, 因为中间再没有改变age值的语句了