ES6+ 剩余参数
1. 前言
上节我们学习了展开语法,本节我们学习与之相反的操作 —— 剩余语法(Rest Syntax 也可以叫剩余参数)看起来和展开语法完全相同都是使用 ... 的语法糖,不同之处在于剩余参数用于解构数组和对象。从某种意义上说,剩余语法与展开语法是相反的:展开语法将数组展开为其中的各个元素,而剩余语法则是将多个元素收集起来成为 一个 整体。
2. 函数 参数
在讲解剩余参数前,我们先来看看,剩余参数在 函数 参数中都 解决 了哪些问题?为什么会引入剩余参数的概念?
在 ES5 中, 函数 经常会传入不定参数,在传入不定参数时,ES5 的给出的 解决方 案是通过 arguments 对象来 获取 函数 调用 时传递的参数。 arguments 对象不是 一个 数组,它是 一个 类数组对象,所谓类数组对象,就是指可以 通过索引 属性 访问元素 并且 拥有 length 属性 的对象。
一个 简单的类数组对象是长这样的:
var arrLike = { : 'name' , : 'age' , : 'job' , length : }
而它所对应的数组应该是这样子的:
var arr = [ 'name' , 'age' , 'job' ] ;
这里我们说类数组对象与数组的性质相似,是因为类数组对象在 访问 、 赋值 、 获取 长度 上的操作与数组是一致的,具体 内容 可查阅相关的类数组使用。
在 函数 体中定义了 Arguments 对象,其包含 函数 的参数和其它 属性 ,以 arguments 变量来指代。下面我们看个实例:
function fn ( ) { console . log ( arguments ) ; } fn ( 'imooc' , , 'ES6' )
在控制台中打印出上面的 代码 结果,如下图所示:在定义 函数 的时候没有给定参数,但是通过 arguments 对象可以拿到传入的参数。可以看到 arguments 中包含了 函数 传递的参数、length 等 属性 ,length 属性 表示的是实参的长度,即 调用 函数 的时候传入的参数个数。这样我们就对 arguments 对象有了一定的了解。
在 ES5 的开发模式下,想要使用传递的参数,则需要按位置把对应的参数取出来。尽管 arguments 是 一个 类数组且可遍历的变量,但它终究不是数组,它 不支持 数组 方法 ,因此我们不能 调用 arguments.forEeach (…) 等数组的 方法 。需要使用一些特殊的 方法 转换成数组使用,如:
function fn ( ) { var arr = [ ] . slice . call ( arguments ) ; console . log ( arr ) } fn ( 'ES6' ) ; // ["ES6"] fn ( 'imooc' , , 'ES6' ) ; // ["imooc", 7, "ES6"]
终于借助 call 方法 把 arguments 转化成 一个 真正的数组了。但是这样无疑是 一个 繁琐的过程,而且不容易理解。这时 ES6 给出了它的完美 解决方 案 —— 剩余参数,那剩余参数是如何在 函数 传参中使用的呢?下面我们来看看实例:
function fn ( ... args ) { console . log ( args ) } fn ( 'ES6' ) ; // ["ES6"] fn ( 'imooc' , , 'ES6' ) ; // ["imooc", 7, "ES6"]
使用方式很简单在 函数 定义时使用 ... 紧接着跟 一个 收集的参数,这个收集的参数就是我们所传入不定参数的集合 —— 也就是数组。这样就很简单地摆脱了 arguments 的束缚。另外,还可以指定 一个 默 认的参数,如下示例:
function fn ( name , ... args ) { console . log ( name ) ; // 基础参数 console . log ( args ) ; // 剩下的参数组成的数组 } fn ( 'ES6' ) ; // 'ES6' // [] fn ( 'imooc' , , 'ES6' ) ; // "imooc" // [7, "ES6"]
上面的 代码 中给 函数 第 一个 参数,声明 一个 变量 name,剩余的参数会被 ... 收集成 一个 数组,这就是剩余参数。引入剩余参数就是为了能替代 函数 内部的 arguments ,由于 arguments 对象不具备数组的 方法 ,所以很多时候在使用之前要先转换成 一个 数组。而剩余参数本来就是 一个 数组,避免了这多余的一步,使用起来既优雅又自然。
2. 解构剩余参数
ES6 允许按照一定模式,从数组和对象中 提取值 ,并对变量进行赋值,这被称为解构(下节我们会讲到)。 比如如下 代码 :
let array = [ , , ] let [ a , b , c ] = array ; console . log ( a ) ; // 1 console . log ( b ) ; // 2 console . log ( c ) ; // 3
再比如如下 代码 :
let obj = { a : , b : , c : } let { a , b , c } = obj ; console . log ( a ) ; // 1 console . log ( b ) ; // 2 console . log ( c ) ; // 3
上面的两个例子,就是数组和对象的解构赋值过程,在解构赋值时,可以使用剩余操作符。剩余操作符所操作的变量会匹配在解构赋值中所有其他变量未匹配到的 属性 。看如下示例:
let { a , b , ... others } = { a : , b : , c : , d : , e : } console . log ( a ) ; // 1 console . log ( b ) ; // 2 console . log ( others ) ; // {c: 3, d: 4, e: 5}
上面的 代码 中,a、b 会匹配对象中对应的值, ...others 则会收集匹配余下的 属性 值,并打包起来构造 一个 新的对象赋值给了 others 。
数组也可以通过剩余操作符,把剩余的元素打包成 一个 新的数组赋值给剩余 属性 , 代码 如下:
let array = [ , , , , ] ; let [ a , b , ... others ] = array ; console . log ( a ) ; // 1 console . log ( b ) ; // 2 console . log ( others ) ; // [3,4,5]
在 函数 传参的时候也可以是和解构一起使用。如下所示。
function fun ( ... [ a , b , c ] ) { return a + b + c ; } fun ( '1' ) // NaN (b 和 c 都是 undefined) fun ( , , ) // 6 fun ( , , , ) // 6 多余的参数不会被 获取 到
上面的 代码 中,a、b、c 会去解构传入参数, 加上 有剩余语法的作用,对应的值从数组中的项解构出来,在 函数 内部直接使用解构出来的参数即可。剩余语法看起来和展开语法完全相同,不同点在于,剩余参数用于解构数组和对象。
3. 小结
本节结合了 ES5 函数 中的 arguments 对象引入了为什么 ES6 会引入剩余参数的概念,可以看到剩余参数所带来的好处。本节 内容 可以总结以下几点:
@H_ 403 _839@剩余参数是为了能替代 函数 内部的 arguments 而引入的; @H_ 403 _839@和展开语法相反,剩余参数是将多个单个元素聚集起来形成 一个 单独的个体的过程。