重温JavaScript(lesson6):函数(2)

又到了我们一起重温JavaScript的时间啦,上一次我们重温了有关函数是一等公民和函数定义方式的内容,这次我们接着看看函数参数相关的内容,这次回顾的部分内容稍微有点晦涩,但是我敢保证你能明白!反正我是信了。

1.函数形参和实参的定义

形参是我们定义函数时所列举的变量。

实参是我们调用函数时所传递给函数的值。

我们看几个拥有形参的函数:

function paramFunc(param) {
 console.log(param);
}
var  paramFunc2 = function(param1,param2) {
 console.log(param1,param2);
}
var paramFunc3 = (param1,param2,param3) => {
 console.log(param1,param2,param3);
}

正如我们所看到的函数的形参是在函数定义时所指定的,而且是无论使用哪种定义函数的方式我们都可以为其指定形参。paramFun是函数声明的情况;paramFunc2是函数表达式的情况;paramFunc3是箭头函数的情况。

另一方面,函数的实参则与函数的调用相联系,它们是函数调用时所传给函数的值。下述代码是对函数的调用:

paramFunc(1);
//1
paramFunc2('hello','world');
//hello world
paramFunc3(1,'hello',{name:'new_name'})
//1 "hello" {name: "new_name"}

将数字1以函数实参的形式传给了函数paramFunc;将字符串hello和world以函数实参的形式传递给paramFunc2;数字1、字符串hello和对象字面量{name: "new_name"}以函数实参的形式传给paramFunc3。

通过上面的例子我们也看到了以下两点:

第一,JS函数定义并未指定形参的类型;

第二,函数的调用也未对实参值做任何类型检查。

实际上JS函数调用甚至不检查传入实参的个数,当实参的数量大于形参的数量时,并不会报错。看代码:

paramFunc(1,2,3)
//1

当函数调用的时提供了多个实参的情况下,第一个实参会赋值给第一形参,第二个实参会赋值给第二个形参,以此类推。如果实参的数量大于形参的数量,那么额外的参数不会赋值给任何形参。那么多余的参数哪去了?我们怎么获取它们呢?首先我们看arguments参数。

2.arguments参数

刚才我们看到了,JS函数的一个特别的地方就是当你给函数多传参数的时候不会造成错误。那是因为函数参数实际上被保存在一个称为arguments的类似数组的对象中。再强调一下,arguments是类似数组的对象而不是数组

arguments对象有两个特点:

第一,arguments可以自动增长来包含任意个数的参数值;

第二,arguments拥有length属性可以告诉我们目前有多少个实参值。

arguments对象自动存在于函数中。下面我们看代码:

function testArguments(param1,param2) {
 console.log(param1,param2);
 //1 "hello"
 console.log(arguments);
 //Arguments(3) [1, "hello", {…}, callee: (...), Symbol(Symbol.iterator): ƒ]
 console.log(arguments.length);
 //3
 console.log(Array.isArray(arguments));
 //false
}
testArguments(1,'hello',{name:'New_Name'});

从代码我们可以看出arguments对象保存了所有传入函数的实参。还有就是我们使用Array.isArray()对arguments对象判断发现它确实不是数组。我们可以用以下的方法将其转换为数组:

var argsArray = Array.prototype.slice.call(arguments)

现在有一个问题,arguments在实际开发中有什么用,有什么应用场景呢?我先脑补一下啥叫“命名参数”哈,命名参数就是在定义函数时指定的形参。

在《JavaScript权威指南 (第6版)》中是这样说的:“arguments对象最适合的场景是在这样的一类函数中,这类函数包括固定个数的命名和必须参数,以及随后个数不定的可选实参。”然后,你们的New_Name就没有看到例子。。。

我们看一个稍微类似的例子吧:假设你想创建一个函数接受任意数量的参数并返回它们的和。因为我们不晓得会有多少个参数,所以无法使用命名参数,在这种情况下,使用arguments是不错的选择。看代码:

function sum() {
 var i = 0, result = 0;
 while(i<arguments.length) {
   result += arguments[i];
   i++;
}
 return result;
}
console.log(sum(1,2));
//3
console.log(sum(3,4,5,6));
//18
console.log(sum(18));
//18
console.log(sum());
//0

咋样?是不是感觉arguments参数还挺香的?ES6引入了一道叫做“剩余参数”的菜,它比arguments还要香,我们稍后再说。关于arguments还有其他两点要说的:

第一,说说函数的length属性和arguments的length属性的异同点;

第二,说说callee和caller。

函数的length属性是什么呢?函数的length属性是用来保存函数期望的参数个数。看代码:

function testFunLength(value) {
 return value;
}
console.log(testFunLength('hello'));
//hello
console.log(testFunLength.length);
//1
console.log(testFunLength('hello','world'));
//hello
console.log(testFunLength.length);
//1
console.log(testFunLength());
//undefined
console.log(testFunLength.length);
//1

从上述代码我们看到,无论是传递几个实参给函数testFunLength,它的length属性都是1,这个值就代表函数定义时命名参数的个数,和你传多少实参,和你传不传实参都没有关系。callee和caller接下来看看callee和caller属性。

刚刚我们看到测试testArguments函数的代码的输出结果中有这样的一句:Arguments(3) [1, "hello", {…}, callee: (...), Symbol(Symbol.iterator): ƒ];这里有callee属性,我在浏览器控制台展开这个属性看到这样的内容:callee: [Exception: TypeError: 'caller', 'callee', and 'arguments' properties may not ...

那么callee是什么呢?ES5规定了在非严格模式下,callee属性指代当前正在执行的函数。而在严格模式下,对callee的读写都会产生类型错误。callee属性在某些时候会非常有用,比如在匿名函数中通过callee来递归地调用自身。而caller表示当前正在执行函数的函数,也就是指向的是函数的调用者,通过caller可以访问调用栈。

说了这么多,我们来对arguments进行一个总结:arguments是指向实参对象的引用,它是一个类数组对象,它拥有像length和callee等可供编程者使用的属性。下面我们就看看ES6引入的“剩余参数”:

3.剩余参数

ES6中新增了一个操作符...。根据用法的不同,可以叫做spread(展开、分散)或rest(剩余)。看代码:

function restExample(a,b) {
 console.log(a,b);
}
restExample(...[1,2]);
//1 2
restExample(...[1,2,3]);
//1 2

我们看到如果将...放在数组之前,该操作符会将数组中的元素分散到函数的各个参数中。在分散的过程中,会忽略掉多余的参数。操作符...另外一种用法正好和上面的例子相反,不是分散,而是将多余的参数汇聚到一起。看下面的代码:

function restExample(a,...b) {
 console.log(a,b);
}
restExample(1,2,3,4,5);
//1 [2, 3, 4, 5]

在此例中,变量b接受了剩余的(rest)所有值。注意:只有函数的最后一个参数才能使用剩余参数。看代码:

function restExample(a,...b,c) {
 console.log(a,b,c);
}
//编译报错

这段代码声明的函数把...放在了不是最后一个参数的b前面,所以编辑没有通过。下面我们看本次分享的最后一个知识点:默认参数

4.默认参数

默认(defaulting)一种很常见的做法,ES6中的一个新特性使可以指定参数的默认值。通常,如果没有给参数传值,它的值将是undefined。默认值则可以给这些参数指定其他的默认值。可以为默认参数赋予任何值,可以是原始类型的数字和字符串或者是布尔值,也可以是对象、数组和函数这样的引用类型。使用默认参数的目的是为了避免空值。

我们对这样的代码一点不陌生:

function add(a,b) {
 a = a || 0;
 b = b || 0;
 return a + b;
}
console.log(add(1));
//1
console.log(add(1,1));
//2

如果函数在调用的时候没有为a,提供对应的值,我们使用||(OR操作符)将两者的默认值设置为0。ES6引入了默认参数后我们可以这样声明add函数:

function add(a=0,b=0) {
 return a + b;
}
console.log(add(1));
//1
console.log(add(1,1));
//2

是不是简洁了很多呢?

好了,今天就到这里吧。本次分享主要介绍了函数参数的内容,虽然我们用了很多篇幅介绍arguments,但是剩余参数和默认参数才是重点哦。下次我们继续一起重温函数调用的内容,我们要碰到充满神秘色彩的this,再见啦~

如有错误,请不吝指正。温故而知新,欢迎和我一起重温旧知识,攀登新台阶~

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重温新知

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值