资讯专栏INFORMATION COLUMN

ES6:Rest 参数和参数默认值【转】

wanglu1209 / 2266人阅读

摘要:在中,引入了一种新方法来指定任意参数的默认值。看下面例子在每个参数的后面是一个表达式,指定了参数未传递时的默认值。如果没有为一个参数指定默认值,那么该参数的默认值为,所以等同于原文链接

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;
}

该实现用到了 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;
}

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

实现Rest参数的函数 restArgs

var restArgs = function (func, startIndex) {
    // rest参数从哪里开始,如果没有,则默认视函数最后一个参数为rest参数
    // 注意, 函数对象的length属性, 揭示了函数的参数个数
    /*
     ex: function add(a,b) {return a+b;}
     console.log(add.length;) // 2
     */
    startIndex = startIndex == null ? func.length - 1 : +startIndex;
    // 返回一个支持rest参数的函数
    return function () {
        // 校正参数, 以免出现负值情况
        var length = Math.max(arguments.length - startIndex, 0);
        // 为rest参数开辟数组存放
        var rest = Array(length);
        // 假设参数从2个开始: func(a,b,*rest)
        // 调用: func(1,2,3,4,5); 实际的调用是:func.call(this, 1,2, [3,4,5]);
        for (var index = 0; index < length; index++) {
            rest[index] = arguments[index + startIndex];
        }
        // 根据rest参数不同, 分情况调用函数, 需要注意的是, rest参数总是最后一个参数, 否则会有歧义
        switch (startIndex) {
            case 0:
                // call的参数一个个传
                return func.call(this, rest);
            case 1:
                return func.call(this, arguments[0], rest);
            case 2:
                return func.call(this, arguments[0], arguments[1], rest);
        }
        // 如果不是上面三种情况, 而是更通用的(应该是作者写着写着发现这个switch case可能越写越长, 就用了apply)
        var args = Array(startIndex + 1);
        // 先拿到前面参数
        for (index = 0; index < startIndex; index++) {
            args[index] = arguments[index];
        }
        // 拼接上剩余参数
        args[startIndex] = rest;
        return func.apply(this, args);
    };
};
参数的默认值

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

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!`;
}

那么,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=undefined) {...}

原文链接

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86772.html

相关文章

  • ES6学习总结(1)

    摘要:返回一个对象,遍历对象自身和继承的所有可枚举属性不含,与相同和在红宝书中就已经提到过属性,表示的是引用类型实例的一个内部指针,指向该实例的构造函数的原型对象。 半个月前就决定要将ES6的学习总结一遍,结果拖延症一犯,半个月就过去了,现在补起来,惭愧惭愧。 阮一峰的《ES6标准入门》这本书有300页左右,除了几个新的API和js语法的扩展,真正有价值的内容并不多。所谓存在即合理,每部分的...

    happyfish 评论0 收藏0
  • ES6 知识整理一(es6快速入门)

    摘要:函数调用会在内存形成一个调用记录,又称调用帧,保存调用位置和内部变量等信息。等到运行结束,将结果返回到,的调用帧才会消失。方法用于将一组值,转换为数组。,和遍历数组方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的方法类似。 ES6 简介 ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。 let 和 const...

    elva 评论0 收藏0
  • es6的解构赋、扩展运算符、rest参数使用

    摘要:模板字符串甚至还能嵌套解构赋值允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免对象的使用。 es6中较为常用的书写风格 为了书写的方便,es6中提出了很多比较友好的书写方式,其中最为常见的属于以下几个: 字符串模板 `abcdef${test}` 解构赋值 let [a, b, c] = [1,...

    xuweijian 评论0 收藏0
  • ES6(2)-各种类型的扩展(字符串、正则、数、函数)

    摘要:返回布尔值,表示参数字符串是否在原字符串的头部。模板字符串之中还能调用函数。其他对字符串还有许多扩展,例如对字符表示的扩充以及为字符串提供了遍历方法详情请点击正则的扩展构造函数在中,构造函数的参数有两种情况。 ES6对各种基本类型都做了扩展,内容有些多,本章节挑选比较重要的扩展说明。 1 字符串的扩展 1.1 includes(), startsWith(), endsWith() 传...

    godruoyi 评论0 收藏0
  • ES6学习笔记2—各扩展

    摘要:字符串的扩展字符的表示法允许采用形式表示一个字符,其中表示字符的码点。返回布尔值,表示参数字符串是否在源字符串的头部。使用和这两个常量,用来表示这个范围的上下限。对于那些无法用个二进制位精确表示的小数,方法返回最接近这个小数的单精度浮点数。 字符串的扩展 字符的 Unicode 表示法 JavaScript 允许采用uxxxx形式表示一个字符,其中xxxx表示字符的 Unicode 码...

    Zoom 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<