JavaScript简餐——细看函数的参数

文章目录


前言

写本《JavaScript简餐》系列文章的目的是记录在阅读学习《JavaScript高级程序设计(第4版)》一书时出现的各个知识点。虽是对读书的笔记和总结,但是希望它轻量、简洁、犀利,不会引起阅读疲劳,可以在碎片化时间和闲暇之余轻巧地沐浴一下知识点。每篇文章只针对一个小部分进行讲解式的梳理,来达到个人复习总结和分享知识的目的。


一、理解参数

  JavaScript中的参数和大多数其他语言都不太一样。函数既不关心传入的参数个数,也不关心这些参数的数据类型。比如你定义函数时要接收一个参数,这并不意味者调用时就得传一个参数。你传一个、两个、三个参数都可以,是不会报错的。之所以会这样,主要是因为ECMAScript函数的参数在内部表现为一个数组。函数被调用时总会接收一个数组,但函数并不关心这个数组中包含什么。如果数组中什么也没有或者数组元素数量超出了函数所要求的参数个数,这都没有问题。当我们在使用function关键字定义函数时,可以在函数内部访问arguments对象,从中取得传进来的每个参数值。   arguments对象是一个类数组对象(但不是Array的实例),因此可以使用中括号语法访问其中的元素。比如第一个参数就是arguments[0],第二个参数就是arguments[1]。而要确定传进来多少个参数可以访问arguments.length属性。话不多说,直接上代码:
function add(x, y) {
  console.log(`一共传进来${arguments.length}个参数哦。`); // 一共传进来2个参数哦。
  return arguments[0] + arguments[1]; 
}
console.log(add(2, 3)); // 5
这里的arguments[0]和arguments[1]就对应着函数参数中指定的x和y。调用函数时传了2和3两个参数,我们也可以看到arguments.length确实是2。 那这时候我们可能会突然有个大胆的想法,我不在function后面的括号里写形参了可不可以???当然可以!来看一下:
function sub() {
  return arguments[0] - arguments[1];
}
console.log(sub(5, 3)); // 2
可见,依然可以拿到参数来进行运算。这就表明,ECMAScript函数的参数只是为了方便才写出来的,并不是必须写出来的。与其他语言不同,在ECMAScript中的形式参数不会创建让之后的调用必须匹配的函数签名。这是因为根本不存在验证形式参数的机制。 arguments对象另一个有意思的地方就是,它的值始终会与对应的形参同步。来看下面的例子:
function add(x, y) {
  console.log(`y是${y}~`); // y是2~
  arguments[1] = 10;
  console.log(`y现在是${y}!`); // y现在是10!
  return x + y;
}
console.log(add(3, 2)); // 13
可以看到,调用函数时传的是2和3,但是在函数中给arguments[1]赋值10之后,传给形参y的值也变成10了,而不是原来的2。说明了,arguments对象的值始终会与对应的形参同步。但这并不意味着它们都访问同一个内存地址,他们在内存中还是分开的,只不过会保持同步而已。 另外还要记住一点:如果只传了一个参数,然后把arguments[1]设置成了10,那么这个值不会反应到第二个形参y上。这是因为arguments对象中的值是你调用函数时实际传的那些参数,而不是定义函数时给出的形式参数。看一下演示:
function add(x, y = 1) {
  console.log(`y是${y}~`); // y是1~
  arguments[1] = 10;
  console.log(`y现在是${y}!`); // y现在是1!
  return x + y;
}
console.log(add(3)); // 4
结果就是y还是我们定义形参时给的默认值1,给arguments对象赋值后y也没有随之同步。 此外,对于形式参数而言,如果调用函数时没有传这个参数,那么它的值就是undefined。这就类似于定义了变量而没有初始化。比如,如果只给add函数传了一个参数,那么y的值就是undefined:
function add(x, y) {
  console.log(y); // undefined
  console.log(arguments[1]); // undefined
}
add(3);
严格模式下,arguments会有一些变化。首先,像前面那样给arguments[1]赋值不会再影响y的值。其次,在函数中尝试重写arguments对象会报错:
    <script>
      "use strict"; // 严格模式
      function add(x, y) {
        console.log(`y是${y}~`); // y是2~
        arguments[1] = 10;
        console.log(`y现在是${y}!`); // y现在是2!
        return x + y;
      }
      console.log(add(3, 2)); // 5
    </script>
    <script>
      "use strict"; // 严格模式
      function add(x, y) {
        console.log(`y是${y}~`);
        arguments = {}; // Uncaught SyntaxError: Unexpected eval or arguments in strict mode
        console.log(`y现在是${y}!`);
        return x + y;
      }
      console.log(add(3, 2));
    </script>

二、箭头函数中的参数

如果函数是使用箭头语法定义的,那么传给函数的参数将不能使用arguments关键字访问,而只能通过定义的形式参数访问:
function add(x, y) {
  console.log(arguments); // [1, 2]
  return x + y;
}

const sub = (x, y) => {
  console.log(arguments); // Uncaught ReferenceError: arguments is not defined
  return x - y;
};

add(1, 2);
sub(5, 3);
虽然箭头函数没有arguments对象,但可以在包装函数中把它提供给箭头函数:
function subFunction(x, y) {
  const sub = () => {
    console.log(arguments); // [5, 3]
    return arguments[0] - arguments[1];
  };
  return sub();
}

console.log(subFunction(5, 3)); // 2
//有点脱裤子放屁的意思呢,哈哈哈。

三、总结

以上就是今天要讲的内容,今天比较详细地介绍了一下函数中的参数以及arguments对象和箭头函数中的特殊情况。下一篇我们来看一下函数中的this关键字。撒花~
上一篇:在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试


下一篇:console.log 带时间输出