深入学习JavaScript中的Rest参数和参数默认值


Posted in Javascript onJuly 28, 2015

本文将讨论使 JavaScript 函数更有表现力的两个特性:Rest 参数和参数默认值。
Rest 参数

通常,我们需要创建一个可变参数的函数,可变参数是指函数可以接受任意数量的参数。例如,String.prototype.concat 可以接受任何数量的字符串作为参数。使用 Rest 参数,ES6 为我们提供一种新的方式来创建可变参数的函数。

我们来实现一个示例函数 containsAll,用于检查一个字符串中是否包含某些子字符串。例如,containsAll("banana", "b", "nan") 将返回true,containsAll("banana", "c", "nan") 将返回 false。

下面是传统的实现方式:

function containsAll(haystack) {
 for (var i = 1; i < arguments.length; i++) {
  var needle = arguments[i];
  if (haystack.indexOf(needle) === -1) {
   return false;
  }
 }
 return true;
}
 
function containsAll(haystack) {
 for (var i = 1; i < arguments.length; i++) {
  var needle = arguments[i];
  if (haystack.indexOf(needle) === -1) {
   return false;
  }
 }
 return true;
}

该实现用到了 arguments 对象,该对象是一个类数组对象,包含函数被调用时的实参列表。这段代码正是我们想要的,但其可读性却不是最优的。函数只有一个形参 haystack,所以不可能一看就知道该函数需要多个参数,并且在遍历 arguments 时,需要特别注意遍历的开始索引为1 ,而不是常见的 0,因为 arguments[0] 就是函数定义时的形参 haystack。如果我们想在 haystack 参数之前或之后添加一些参数,我们不得不更新内部的循环。Rest 参数解决了这些问题,下面是 使用 Rest 参数的实现方式:

function containsAll(haystack, ...needles) {
 for (var needle of needles) {
  if (haystack.indexOf(needle) === -1) {
   return false;
  }
 }
 return true;
}
 
function containsAll(haystack, ...needles) {
 for (var needle of needles) {
  if (haystack.indexOf(needle) === -1) {
   return false;
  }
 }
 return true;
}

以上两个实现都满足了我们的需求,但后者包含一个特殊的 ...needles 语法。我们来看看调用containsAll("banana", "b", "nan") 时的细节,参数 haystack 和以往一样,将用函数的第一个实参填充,值为 "banana",needles 前面的省略号表示它是一个 Rest 参数,剩余的所有实参将被放入一个数组中,并将该数组赋给 needles 遍量。在这个调用中,needles 的值为 ["b", "nan"]。然后,就是正常的函数执行了。

只能将函数的最后一个函数作为 Rest 参数,在函数被调用时,Rest 参数之前的参数都将被正常填充,之外的参数将被放入一个数组中,并将该数组作为 Rest 参数的值,如果没有更多的参数,那么 Rest 参数的值为一个空数组 [],Rest 参数的值永远都不会是 undefined。
参数的默认值

通常,调用一个函数时,不需要调用者传递所有可能的参数,那些没有传递的参数都需要一个合理的默认值。JavaScript 对那些没有传递的参数都有一个固定的默认值 undefined。在 ES6 中,引入了一种新方法来指定任意参数的默认值。

看下面例子:

function animalSentence(animals2="tigers", animals3="bears") {
  return `Lions and ${animals2} and ${animals3}! Oh my!`;
}
 
function animalSentence(animals2="tigers", animals3="bears") {
  return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

在每个参数的 = 后面是一个表达式,指定了参数未传递时的默认值。所以,animalSentence() 返回 "Lions and tigers and bears! Oh my!", animalSentence("elephants") 返回"Lions and elephants and bears! Oh my!", animalSentence("elephants", "whales") 返回 "Lions and elephants and whales! Oh my!"。

参数默认值需要注意的几个细节:

    与 Python 不一样的是,参数默认值的表达式是在函数调用时从左到右计算的,这意味着表达式可以使用前面已经被填充的参数。例如,我们可以将上面的函数变得更有趣一点:

function animalSentenceFancy(animals2="tigers",
  animals3=(animals2 == "bears") ? "sealions" : "bears")
{
 return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

 
function animalSentenceFancy(animals2="tigers",
  animals3=(animals2 == "bears") ? "sealions" : "bears")
{
 return `Lions and ${animals2} and ${animals3}! Oh my!`;
}

那么,animalSentenceFancy("bears") 将返回 "Lions and bears and sealions. Oh my!"。

    传递 undefined 等同于没有传递该参数。因此,animalSentence(undefined, "unicorns") 将返回 "Lions and tigers and unicorns! Oh my!"。
    如果没有为一个参数指定默认值,那么该参数的默认值为 undefined,所以

function myFunc(a=42, b) {...}
 
function myFunc(a=42, b) {...}

等同于

function myFunc(a=42, b=undefined) {...}
 
function myFunc(a=42, b=undefined) {...}

抛弃 arguments

通过 Rest 参数和参数的默认值,我们可以完全抛弃 arguments 对象,使我们的代码可读性更高。此外,arguments 对象也加深了优化 JavaScript 的难题。

希望以上两个新特性可以完全取代 arguments。作为第一步,在使用 Rest 参数或参数的默认值时,请避免使用 arguments 对象,假如 arguments 对象还不会立即被移除,或者永远不会,那么也最好是避免在使用 Rest 参数或参数默认值时使用 arguments 对象。
兼容性

Firefox 15 以上的版本已经支持这两个新特性。然而,除此之外,还没有其他任何浏览器支持。最近,V8 的实验环境添加了对 Rest 参数的支持,而参数默认值还有一个 issue,JSC 也对 Rest 参数和参数默认值提了一些 issue。

Babel 和 Traceur 这两个编译器都已经支持了参数默认值,所以你可以大胆使用。
结论

尽管从技术层面上看,这两个新特性在并没有给函数引入新的行为,但它们可以使一些函数的声明更具表现力和可读性。

Javascript 相关文章推荐
双击滚屏-常用推荐
Nov 29 Javascript
css图片自适应大小
Nov 28 Javascript
JavaScript 原型链学习总结
Oct 29 Javascript
探讨在JQuery和Js中,如何让ajax执行完后再继续往下执行
Jul 09 Javascript
jquery限定文本框只能输入数字即整数和小数
Nov 29 Javascript
JavaScript中的console.time()函数详细介绍
Dec 29 Javascript
jQuery中prop()方法用法实例
Jan 05 Javascript
实例详解angularjs和ajax的结合使用
Oct 22 Javascript
jquery.form.js异步提交表单详解
Apr 25 jQuery
vue2.0的contextmenu右键弹出菜单的实例代码
Jul 24 Javascript
JavaScript实现异步图像上传功能
Jul 12 Javascript
vue 项目接口管理的实现
Jan 17 Javascript
JQuery实现鼠标滚轮滑动到页面节点
Jul 28 #Javascript
CSS3实现动态背景登录框的代码
Jul 28 #Javascript
javascript制作幻灯片(360度全景图片)
Jul 28 #Javascript
详解JavaScript ES6中的模板字符串
Jul 28 #Javascript
javascript解决IE6下hover问题的方法
Jul 28 #Javascript
JavaScript如何自定义trim方法
Jul 28 #Javascript
详解JavaScript ES6中的Generator
Jul 28 #Javascript
You might like
漫荒推荐:画风超赞的国风漫画推荐 超长假期不无聊
2020/03/08 国漫
PHP 删除一个目录及目录下的所有文件的函数代码
2010/05/26 PHP
PHP常用的文件操作函数经典收藏
2013/04/02 PHP
探讨php中遍历二维数组的几种方法详解
2013/06/08 PHP
php实现过滤表单提交中html标签的方法
2014/10/17 PHP
PHP中preg_match正则匹配中的/u、/i、/s含义
2015/04/17 PHP
基于php的CMS中展示文章类实例分析
2015/06/18 PHP
PHP 读取大文件并显示的简单实例(推荐)
2016/08/12 PHP
Thinkphp事务操作实例(推荐)
2017/04/01 PHP
PHP在弹框中获取foreach中遍历的id值并传递给地址栏
2017/06/13 PHP
php实现的证件照换底色功能示例【人像抠图/换背景图】
2020/05/29 PHP
JavaScript 动态改变图片大小
2009/06/11 Javascript
jquery text()要注意啦
2009/10/30 Javascript
JavaScript版TAB选项卡效果实例
2013/08/16 Javascript
javascript事件委托的方式绑定详解
2015/06/10 Javascript
js实现随屏幕滚动的带缓冲效果的右下角广告代码
2015/09/04 Javascript
jQuery层级选择器_动力节点节点Java学院整理
2017/07/04 jQuery
从源码看angular/material2 中 dialog模块的实现方法
2017/10/18 Javascript
基于vue监听滚动事件实现锚点链接平滑滚动的方法
2018/01/17 Javascript
解决Vue+Element ui开发中碰到的IE问题
2018/09/03 Javascript
浅谈Vue数据响应思路之数组
2018/11/06 Javascript
shelve  用来持久化任意的Python对象实例代码
2016/10/12 Python
浅谈Python Opencv中gamma变换的使用详解
2018/04/02 Python
Python 十六进制整数与ASCii编码字符串相互转换方法
2018/07/09 Python
基于Python实现剪切板实时监控方法解析
2019/09/11 Python
Python中生成一个指定长度的随机字符串实现示例
2019/11/06 Python
Pycharm连接远程服务器过程图解
2020/04/30 Python
CSS3实现大小不一的粒子旋转加载动画
2016/04/21 HTML / CSS
美国沙龙美发产品购物网站:Hair.com by L’Oreal
2020/11/09 全球购物
高中班长自我鉴定
2013/12/20 职场文书
白银帝国观后感
2015/06/17 职场文书
给校长的建议书作文500字
2015/09/14 职场文书
2016廉洁教育心得体会
2016/01/20 职场文书
使用pycharm运行flask应用程序的详细教程
2021/06/07 Python
MySQL学习必备条件查询数据
2022/03/25 MySQL
vue实现登陆页面开发实践
2022/05/30 Vue.js