JavaScript 之 函数

创建函数

函数结构需要符合一定的语法规范,当前创建函数有三种方式:

  1. 函数声明方式。
  2. 函数表达式方式。
  3. 利用Function构造函数。

函数声明方式

语法规则:

1
function 函数名称 (参数:可选){ 函数体 }
  1. 函数声明不是一个可以执行的语句,所以不以分号结束。
  2. 函数声明方式创建函数,函数名称是必须的,表达式方式则是可选的
  3. 函数名称后面小括号是必须的。
  4. 参数是可选的。
  5. 大括号中内容可以为空,但是大括号不能省略。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 普通函数定义
function f1() {
console.log("Hello world!");
}

// 带参数的函数
function f2(a, b) {
console.log(arguments); // 内置的arguments对象
console.log(arguments.length);
console.log(a, b);
}

// 带返回值的函数
function sum(a, b){
return a + b;
}
sum(1, 2); // 调用函数

表达式方式声明

表达式方式声明的本质是:函数可以通过一个表达式定义,然后储存在变量中

语法规则:

1
function 函数名称(可选) (参数:可选){ 函数体 }
  1. 表达式方式声明的函数,名称是可选的,这一点与函数声明方式不同。
  2. 以分号结尾,因为这是一个可执行的语句。

代码示例:

1
2
3
let func=function funcName(a){
console.log(a);
}

上述代码就是一个赋值表达式,右侧函数带有名字,最常见的是没有名字(匿名)的形式:

1
2
3
let func=function(a){
console.log(a);
}

自执行函数

  1. !function(形参列表){}(实参列表), 可以使用多种运算开头, 但一般用感叹号!
1
2
3
4
5
6
7
!function(){
console.log(789)
}();

!function(num){
console.log(num)
}(789);
  1. (function(形参列表){}(实参列表)), 使用()将函数及函数后设的小括号包裹起来。
1
2
3
4
5
6
7
(function(){
console.log(789)
}())

(function(num){
console.log(num)
}(789))
  1. (function(){})(), 使用 () 值包裹函数部分。
1
2
3
4
5
6
7
(function(){
console.log(789)
})()

(function(num){
console.log(num)
})(789)
三种写法的特点:
  1. 使用 ! 开头, 结构清晰, 不容易混乱, 推荐使用;
  2. 能够表明匿名函数与调用的 () 为一个整体, 官方推荐使用;
  3. 无法表明函数与之后的 () 的整体性, 不推荐使用;

箭头函数

ES2015(_ES6)新增箭头函数,全部是匿名函数,也就是通过表达式方式声明。

1
2
3
4
5
var f = v => v;
// 等同于
var f = function(v){
return v;
}

如果箭头函数不需要参数或需要多个参数,就是用圆括号代表参数部分:

1
2
3
4
5
6
7
8
9
var f = () => 5;
// 等同于
var f = function(){return 5};

var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2){
return num1 + num2;
}

Function构造函数方式

function函数自身也是一个对象实例,它可以由内置的JavaScript函数构造器Function构造函数创建。

1
2
var myFunction = new Funtion("a", "b", "return a * b");
var x = myFuntion(4 ,3);

也可以写成

1
var myFunction = function(a, b){return a * b}

函数的调用与返回值

函数名称+()可以调用指定函数,通过小括号可以传递参数。对于任何方式声明的函数都适用,代码实例如下:

1
2
3
4
5
function func(a,b){
let num=a+b;
return num;
}
console.log(func(1,2));

通过语句可以返回一个值,如果没有return语句,相当于返回undefined。

函数的参数和关键字

arguments关键字

arguments用于储存调用函数时的所有实参, arguments是一个数组, 索引从 0 开始, 表示实参的个数, arguments.callee() 调用函数本身,arguments.length 属性返回函数调用过程接受到的参数个数
arguments关键字:只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。利用arguments,你可以获得调用者传入的所有参数。也就是说,即使函数不定义任何参数,还是可以拿到参数的值

1
2
3
4
5
6
7
8
9
10
11
function abs() {
if (arguments.length === 0) {
return 0;
}
var x = arguments[0];
return x >= 0 ? x : -x;
}

abs(); // 0
abs(10); // 10
abs(-9); // 9

rest参数

  • 由于JavaScript函数允许接收任意个参数,于是我们就不得不用arguments来获取所有参数:

  • ES6标准引入了rest参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function 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参数只能写在最后,前面用... 标识,从运行结果可知,传入的参数先绑定ab,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

如果传入的参数连正常定义的参数都没填满,也不要紧,rest参数会接收一个空数组(注意不是undefined)。

函数返回值

函数只能返回一个值,如果要返回多个值,只能将其放在数组或对象中返回。

函数的全局变量和局部变量(理解即可)

局部变量: 在JavaScript函数内部声明的变量(使用 var)是局部变量,所以只能在函数内部访问它(该变量的作用域是函数内部)。只要函数运行完毕,本地变量就会被删除。

全局变量: 在函数外声明的变量是_全局_变量,网页上的所有脚本和函数都能访问它。

变量生存周期:

  • JavaScript变量的生命期从它们被声明的时间开始。
  • 局部变量会在函数运行以后被删除。
  • 全局变量会在页面关闭后被删除。

作用域

首先在函数内部查找变量,找不到则到外层函数查找,逐步找到最外层。

几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 1.
var city = "BeiJing";
function f() {
var city = "ShangHai";
function inner(){
var city = "ShenZhen";
console.log(city);
}
inner();
}

f(); //输出结果是?ShenZhen


// 2.
var city = "BeiJing";
function Bar() {
console.log(city);
}
function f() {
var city = "ShangHai";
return Bar;
}
var ret = f();
ret(); // 打印结果是?BeiJing


// 3.闭包
var city = "BeiJing";
function f(){
var city = "ShangHai";
function inner(){
console.log(city);
}
return inner;
}
var ret = f();
ret();

词法分析(尝试理解)

JavaScript中在调用函数的那一瞬间,会先进行词法分析。

词法分析的过程:

当函数调用的前一瞬间,会先形成一个激活对象:Avtive Object(AO),并会分析以下3个方面:

  1. 函数参数,如果有,则将此参数赋值给AO,且值为undefined。如果没有,则不做任何操作。
  2. 函数局部变量,如果AO上有同名的值,则不做任何操作。如果没有,则将此变量赋值给AO,并且值为undefined。
  3. 函数声明,如果AO上有,则会将AO上的对象覆盖。如果没有,则不做任何操作。

函数内部无论是使用参数还是使用局部变量都到AO上找。看两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var age = 18;
function foo(){
console.log(age);
var age = 22;
console.log(age);
}
foo(); // 问:执行foo()之后的结果是?undefined 和 22


// 第二:
var age = 18;
function foo(){
console.log(age);
var age = 22;
console.log(age);
function age(){
console.log("呵呵");
}
console.log(age);
}
foo(); // 执行后的结果是?ƒ age(){console.log("呵呵");} 和 22 和22

词法分析过程:

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值的语句了


JavaScript 之 函数
https://flepeng.github.io/021-frontend-02-JavaScript-JavaScript-之-函数/
作者
Lepeng
发布于
2021年7月5日
许可协议