3个点操作符

剩余参数

假设我们有这样的一个Object,存储学生年龄,如下:

var studentInfo = {
    '张飞' :  18,
    '赵云' :  17,
    '许褚' :  16,
    '典韦' :  18,
    '关羽' :  19,
}

要实现一个函数 printAge ,它的输入参数是一些学生的姓名,函数需要打印出这些输入学生的年龄。

这里有个问题,就是调用printAge 时, 输入的学生名字 个数是不固定的,可能是1个、2个、100个, 甚至也可能是0个。

这肯定难不倒聪明的你, 你会使用数组作为参数类型, 数组里面每个元素存放要获取年龄信息的学生名。

像这样

var studentInfo = {
    '张飞' :  18,
    '赵云' :  17,
    '许褚' :  16,
    '典韦' :  18,
    '关羽' :  19,
}

function  printAge(students){
    for (let student of students) {
        console.log(`学生:${student} , 年龄 ${studentInfo[student]}`);
    }
}

printAge(['张飞', '典韦', '关羽'])
printAge(['赵云'])

运行一下看看, 完全没有问题。



但是我们似乎觉得,如果调用该函数的时候,能把下面这种写法

printAge(['张飞', '典韦', '关羽'])

改为

printAge('张飞', '典韦', '关羽')

这样调用。 显得更加易读易懂。

但是问题就在于,因为这个函数参数的 个数是不确定的 ,函数如何定义呢?

js中可以使用 , 使用三个点操作符 ... , 这样定义

function  printAge(...students){
    for (let student of students) {
        console.log(`学生:${student} , 年龄 ${studentInfo[student]}`);
    }
}

和上面相比,唯一的改动就是, 参数名前面加了3个点。

然后我们就可以这样调用该函数了

printAge('张飞', '典韦', '关羽')
printAge('赵云')

效果和前面一种定义完全一样。

这种前面加 3个点 的参数,称之为 剩余参数(rest parameters)

执行函数时,它是一个 数组 ,里面存放所有 所有未被对应的 剩余参数值。

也就是说,上面的例子里面,如下调用代码执行的时候,

printAge('张飞', '典韦', '关羽')

js解释器创建一个 数组 赋值给这个 students,里面存放了 ‘张飞’, ‘典韦’, ‘关羽’ 三个字符串对象作为元素,


我们在代码里加入一行语句,显示 students 参数

var studentInfo = {
    '张飞' :  18,
    '赵云' :  17,
    '许褚' :  16,
    '典韦' :  18,
    '关羽' :  19,
}

function  printAge(...students){
    console.log(students)
    for (let student of students) {
        console.log(`学生:${student} , 年龄 ${studentInfo[student]}`);
    }
}

printAge('张飞', '典韦', '关羽')

运行结果显示

[ '张飞', '典韦', '关羽' ]
学生:张飞 , 年龄 18
学生:典韦 , 年龄 18
学生:关羽 , 年龄 19

说明 students变量确实是对应一个数组。



剩余参数往往不是单独出现在函数参数中的,可以和其它参数一起出现

比如

var studentInfo = {
    '张飞' :  18,
    '赵云' :  17,
    '许褚' :  16,
    '典韦' :  18,
    '关羽' :  19,
}

function  printAge(prefix, ...students){
    console.log(students)
    for (let student of students) {
        console.log(`${prefix} 学生:${student} , 年龄 ${studentInfo[student]}`);
    }
}

printAge('-->', '张飞', '典韦', '关羽')
printAge('==>', '张飞', '典韦', '关羽')

这时,先把其它参数赋值完毕, 剩余的参数对象 放到 剩余参数对应的数组中。

展开语法

printAge 使用上面这样的剩余参数,假如我们要传入的参数恰好已经在一个数组 中,比如

var onebatch = ['张飞', '典韦', '关羽']

怎么调用printAge呢?

当然可以这样

printAge (onebatch[0], onebatch[1], onebatch[2])

显然不方便。

其实,我们还可以使用三个点操作符 ... 可以这样

printAge(...onebatch)

在调用时,这就是 展开语法(Spread syntax)

我们可以将其等价于

printAge (onebatch[0], onebatch[1], onebatch[2])



数据展开,不仅仅使用在调用函数的时候。

比如

var batch1 = ['张飞', '典韦', '关羽']
var batch2 = [...batch1, '赵云', '马超']
console.log(batch2)

这样, batch2 的前3个元素就是 batch1,里面的元素。



数据展开,还可以用于 Object,比如

var studentInfo1 = {
    '张飞' :  18,
    '赵云' :  17,
    '许褚' :  16,
    '典韦' :  18,
    '关羽' :  19,
}

var studentInfo2 = {
    ...studentInfo1,
    '黄忠' :  78,
    '张辽' :  39,
}

这样,studentInfo2的值为

{
  '张飞': 18,
  '赵云': 17,
  '许褚': 16,
  '典韦': 18,
  '关羽': 19,
  '黄忠': 78,
  '张辽': 39
}

可以多次使用数据展开,比如

var studentInfoAll = {
    ...studentInfo1,
    ...studentInfo2
}

这样可以实现多个Object 的合并,其中重复的数据会被后面的Object里面的值替换。


注意

var studentInfo = {
    '黄忠' :  78,
    '张辽' :  39
}

// 赋值1
var studentInfoAll_clone1 = studentInfo

// 赋值2
var studentInfoAll_clone2 = { ...studentInfo}

两个赋值语句,看起来好像最终内容相同,但是效果有很大差别。

赋值1 语句写法,studentInfoAll_clone1 只是 对象的新变量名, 和 studentInfo 对应的是同一个数据对象

而 赋值1 语句写法,studentInfoAll_clone2 对应的是一个新的对象。

studentInfoAll_clone1 === studentInfo // 结果为true
studentInfoAll_clone2 === studentInfo // 结果为false