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

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. 函数名(实参,实参)

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

图片4.png

5.4 函数返回值 (return)

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

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

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

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

break、continue、return的区别

  • break :结束当前的循环体(如 for、while)
  • continue :跳出本次循环,继续执行下次循环(如 for、while)
  • 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[1]);
  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. }

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(num1, num2) {
  4. return num1 + num2;
  5. }

以上代码可以正常运行,因为函数声明会在任何代码执行之前先被读取并添加到执行上下文。这个过程叫作函数声明提升(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. };