函数:就是封装了一段可被重复调用执行的代码块。通过次代码块可以实现大量代码的重复使用

5.1 声明函数

  • 函数对任何语言来说都是核心组件,因为它们可以封装语句,任何在任何地方、任何时间执行。ECMAScript中的的函数使用function关键字声明,此关键字小写,后跟一组参数,然后是函数体。
  • 函数的命名应该有意义,即能通过函数名来了解此函数的作用。

    1. function functionName(arg0,arg1,···,argN) {
    2. statements
    3. }
    4. function 函数名(形参0, 形参1, ···, 新参N) {
    5. // 函数内部代码
    6. }

    5.2 调用函数

    函数在声明后可以进行调用,调用没有次数限制,但先后顺序会影响结果

    1. 函数名(实参,实参); //函数调用时通过函数名来使用
  • 调用函数时不要忘记后面的()

  • 函数需要通过调用来执行
  • 函数内部声明的变量为局部变量,在函数外部无法使用,需要通过return来返回借给函数带到全局环境下。

    5.3 形参和实参

  • 形参:时函数在构建声明时模拟带入的变量,不做声明,也没有任何实意

  • 实参:字面意义,在调用函数时,带入函数内部的参数变量,通常在外部声明

    1. // 构建函数
    2. function 函数名(形参,形参) {
    3. 函数内部代码
    4. }
    5. //调用函数
    6. 函数名(实参,实参)

    函数形参和实参数量不匹配时

    5. 函数 - 图1

    5.4 函数返回值

    函数尽量不要再内部进行输出,尤其在进行多个函数参与情况下建议函数只需返回运算结果

    1. function 函数名(形参,形参){
    2. 函数运算;
    3. return 返回给函数的值
    4. }
  • 在使用 return 语句时,函数会停止执行,并返回指定的值

  • 如果函数没有 return,返回的值是 undefined

    1. function sun(num1, num2) {
    2. return num1 + num2; //将两个数的和返回给函数
    3. console.log(num1); //return 后面的函数内部代码不会被执行
  • return只能返回一个值,如果填写多个值,只会返回最后一个逗号后面的值

  • 如果需要返回多个值,可以使用数组进行多个值的返回
  • 也可以通过对象(object)来返回多个值

    break、continue、return的区别

  • break:结束当前的循环体(如 for、while)

  • continue:跳出本次循环,继续继续下次循环(如 for、whlie)
  • return:不仅可以退出循环,还能够返回 return 语句中的值,同时还可以结束当前的函数体内的代码

    5.5 arguments

    当不确定有多少个参数传递的时候,可以用arguments来获取。在 JavaScript 中,arguments实际上它时当前函数的一个内置对象。所有函数都内置了一个 arguments 对象,arguments 对象中存储了传递的所有的实参,arguments将用户输入的参数以数组的方式存储起来,在使用时用arguments[]的方式来使用。

    1. function doAdd() {
    2. if(arguments.length === 1) {
    3. console.log(arguments[0] + 10);
    4. } else if(arguments.length === 2) {
    5. console.log(arguments[0] + arguments);
    6. }
    7. }
    8. doAdd(10); //20
    9. doAdd(30, 20); //50

    arguments展示形式时一个伪数组,因此可以进行遍历。伪数组具有以下特点:

  • 具有 length 属性

  • 按索引方式存储数据
  • 不具有数组的 push,pop 等方法

arguments可以配合形参使用:

  1. function doAdd(num1, num2) {
  2. if(arguments.length === 1) {
  3. console.log(num1 + 10);
  4. } else if(arguments.length === 2) {
  5. console.log(arguments[0] + num2);
  6. }
  7. }
  1. arguments 对象的另一个有意思的地方就是,它的值始终会与对应的命名参数同步。来看下面的例子:
  1. function doAdd(num1, num2) {
  2. arguments[1] = 10;
  3. console.log(arguments[0] + num2);
  4. }
  • 这个 doAdd() 函数把第二个参数的值重写为 10。因为 arguments 对象的值会自动同步带对应的命名参数,所以修改 arguments[1]也会修改 num2 的值,因此两者的值都是 10。
  • 但这并不意味着它们都访问同一个内存地址,它们在内存中还是分开的,只不过会保持同步而已。
  • 另外还要记住一个点:如果只传了一个参数。然后把 arguments[1] 设置为某个值,那么这个值并不会反映到第二个命名参数。这是因为 arguments 对象的长度是根据传入的参数个数,而非定义函数时给出的命名参数个数确定的。
  • 对于命名参数而言,如果调用函数时没有穿这个参数,那么它的值就是 undefined。这就类似于定义了变量而没有初始化。比如,如果只给 doAdd()穿了一个参数,那么 num2 的值就是 undefined。

注意:在函数内部使用该对象,用此对象获取函数调用时传的实参。

5.6 函数可以进行多次的嵌套

函数内部可以调用另一个函数,在同一作用域代码中,函数名即代码封装的操作,使用函数名加括号既可以将封装的操作执行。

5.7 函数的两者声明方式

事实上,JavaScript 引擎在加载数据时对它们是区别对待的。JavaScript 引擎在任何代码执行之前,会先读取函数声明,并在执行上下文中生成函数定义。而函数表达式必须等到代码执行到它那一行,才会在执行上下文中生存函数定义。

来看下面的例子:

  1. // 没问题
  2. console.log(sum(10,10));
  3. function sum(sum1,sum2) {
  4. retun num1 + num2;
  5. }
  1. 以上代码可以正常运行,因为函数声明会在任何代码执行之前先被读取并添加到执行上下文。这个过程叫做函数声明提升(function declaration hoisting)。

在执行代码是,JavaScript 引擎会先执行一边扫描,把发现的函数声明提升到源代码树的顶部。因此即使函数定义出现在调用它们的代码之后,引擎也会把函数声明提升到顶部。如果把前面代码中的函数声明改为等价的函数表达式,那么执行的时候就会出错:

  1. // 会出错
  2. console.log(sum(10,10));
  3. let sum = function(num1, num2) {
  4. return num1 + num2;
  5. };

上面的代码之所以会出错,是因为这个函数定义包含在一个变量初始化语句中,而不是函数声明中。这意味着代码如果没有执行到这一行,那么值上下文就没有函数的定义,所以上面的代码会出错。这并不是因为使用 let 而导致的,使用 var 关键之也会碰到同样的问题:

  1. console.log(sum(10, 10));
  2. var sum = function(num1, num2) {
  3. return num1 + num2;
  4. };