javascript中rest参数和展开运算符的使用

IT技术 javascript ecmascript-6 spread-syntax rest-parameters
2021-03-06 07:31:19

ECMAScript 6 中将添加的 rest 参数的用途是什么?

例如,在 ECMAScript 5 中,您可以执行以下操作以获取从第二个元素开始的参数数组:

// ES 5
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name) {
  var items = [].slice.call(arguments, 1); //['money'] in first case
  items.forEach(function (item) {
    vault.customer[name].push(item);
  });
}

这将等同于 ECMAScript 6 中的以下代码:

// ES 6
store('Joe', 'money');
store('Jane', 'letters', 'certificates');
function store(name, ...items) {
  items.forEach(function (item) {
    vault.customer[name].push(items)
  });
}

它们之间的区别只是语法还是存在性能问题?

也适用于扩展运算符 (...)

//in ecmascript5
var max = Math.max.apply(null, [14, 3, 77]);
//but in ecmascript6
var max = Math.max(...[14, 3, 77]);

这只是语法更改还是性能问题?

6个回答

它们之间的区别只是语法还是存在性能问题?

两者,还有更多……

休息参数:

  1. 其他语言(Ruby、Python)中已知习语
  2. 易于阅读和维护(相对于slice)。
  3. 比较容易理解,适合初学者。
  4. 可以(并且可能会)带来更好的性能,因为引擎可以优化。
  5. 工具友好,因为他们可以静态分析。

除了@kangax 的回应之外,我还要详细说明多次arguments调用对象时性能和正确性是有问题的如果您将arguments对象传递给另一个函数,则您将修改范围内相应局部变量的权利传递给它

function foo(arg) {
    bar(arguments);
    return arg;
}

function bar(args) {
    args[0] = 20;
}

foo(10) // 20

这个特性的存在使函数内部的局部推理失效,这使得 JIT 优化更加困难,并引入了一定的安全隐患。为速度而设计的程序必须使用比Array.prototype.slice.call(arguments)惯用语复杂得多的样板代码来解决这个问题,并且在安全上下文中,arguments必须严格禁止传递

无需使用arguments对象来提取可变参数是新语法的众多优势之一。

在您给定的示例中,您直接传递对象文字。虽然仍然是语义的,但它似乎与扩展运算符的应用不同。IMO,当列出每个参数会影响代码可读性(和可维护性,当替换传统的 push、splice、concat 方法时),spread 运算符的值变得明显。

使用扩展运算符

let nums = [1.25,5.44,7.15,4.22,6.18,3.11];   

function add(a, b, ...nums){
  let sum = parseInt(a + b);
  nums.forEach(function(num) {
    sum += num;
  })
  return parseInt(sum);
}

console.log(add(1, 2, ...nums)); //30

ES6Fiddle 演示(由traceur-compiler 编译

没有传播运算符

var nums = [1.25,5.44,7.15,4.22,6.18,3.11];   

function add(a, b, nums){
  var sum = parseInt(a + b);
  nums.forEach(function(num) {
    sum += num;
  })
  return parseInt(sum);
}

console.log(add(1, 2, nums)); //30

JSFiddle 演示

最后,我不认为使用扩展运算符会提高或阻碍性能,但它确实提供了改进的代码可读性,以及可以说的可维护性。遗憾的是,ES6 还没有被广泛实现,我可以断言,浏览器支持需要一段时间才能实现。

有趣的事实:PHP 5.6 引入了类似的功能及其可变参数函数,这将导致func_get_args()冗余。

一个rest参数将收集各个参数到一个数组,当你在一个函数参数定义使用的点,而传播经营者使用一个点时,扩展的阵列到各个参数function call

当您想将单个参数收集到数组中时,您可以rest在函数参数定义中使用运算符。

let sum = (...numbers) => {
  let result = 0;

  numbers.forEach(function(n){
  result += n;
  });

  return result;
};

console.log(sum(1,2,3));

处理arguments嵌套函数内部的对象变得非常棘手,让我们看看下面的情况,我们的内部函数需要访问传递的arguments对象。

如果我们inner function filterNumbers需要访问参数,在variable进一步传递它之前必须将其存储在上面,因为每个函数都有自己的arguments对象,它是一个类似对象的数组。

function sumOnlyNumbers() {
  var args = arguments;
  var numbers = filterNumbers();
  return numbers.reduce((sum, element) => sum + element);
    function filterNumbers() {
      return Array.prototype.filter.call(args,
        element => typeof element === 'number'
    );
  }
}
sumOnlyNumbers(1, 'Hello', 5, false);

该方法有效,但过于冗长。var args = arguments可以省略并且Array.prototype.filter.call(args)可以转换为args.filter()使用rest参数。

function sumOnlyNumbers(...args) {
  var numbers = filterNumbers();
  return numbers.reduce((sum, element) => sum + element);
    function filterNumbers() {
      return args.filter(element => typeof element === 'number');
    }
  }
sumOnlyNumbers(1, 'Hello', 5, false); // => 6

当您想将数组扩展为单个参数时,您可以在函数调用中使用扩展运算符。

let sum = (a,b,c) => {
  return a + b + c;
};

console.log(sum(...[1,2,3]));

在上面的代码中,扩展运算符将“spread”跨越parameters a, b, and c. spread操作者也可以扩大的阵列创建的个体元件array文字。

var a = [4, 5, 6];
var b = [1, 2, 3, ...a, 7, 8, 9]
console.log(b);

改进了 ES6 中的函数调用

ES5.apply()在函数对象上提供方法来解决这个问题。不幸的是,这种技术有 3 个问题:

  • 有必要手动指示函数调用的上下文
  • 不能在构造函数调用中使用
  • 更短的解决方案更可取

.apply()第二次指出上下文国家似乎无关紧要,使其更加冗长。

let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push.apply(countries, otherCountries);
console.log(countries); // => ['India', 'USA', 'China', 'Japan']

spread运营商填补了function调用参数从一个值array让我们使用扩展运算符改进上述示例:

let countries = ['India', 'USA'];
let otherCountries = ['China', 'Japan'];
countries.push(...otherCountries);
console.log(countries); // => ['Moldova', 'Ukraine', 'USA', 'Japan']

Spread运算符从数组配置构造函数调用参数,直接使用.apply().

class Actor {
  constructor(name, country) {
  this.name = name;
  this.country = country;
  }
  getDescription() {
  return `${this.name} leads ${this.country}`;
  }
}
var details = ['RajiniKanth the Great', 'India'];
var Alexander = new Actor(...details);
console.log(Alexander.getDescription()); // => 'RajiniKanth the Great leads India'

此外,您可以spread在同一调用中组合多个运算符和常规参数。以下示例从数组中删除现有元素,然后添加其他数组和一个元素:

var numbers = [1, 2];
var evenNumbers = [4, 8];
const zero = 0;
numbers.splice(0, 2, ...evenNumbers, zero);
console.log(numbers); // => [4, 8, 0]

克隆一个数组实例:

var words = ['Hi', 'Hello', 'Good day'];
var otherWords = [...words];
console.log(otherWords); // => ['Hi', 'Hello', 'Good day']
console.log(otherWords === words); // => false

otherWords是 words 数组的克隆版本。请注意,克隆仅发生在数组本身上,而不发生在包含的元素上(即它不是深度克隆)。

参考资料:https : //dmitripavlutin.com/how-three-dots-changed-javascript/

@DmitriPavlutin:检查我更新的帖子,它收集了很多东西......也许我应该发布一个参考。
2021-05-12 07:31:19

我认为 rest 参数的主要区别是:

  • ECMA5 参数中的参数就像数组而不是数组,因此数组的普通方法不可用,但在 ECMA6 参数中是数组。
  • 我认为在 ECMA5 代码示例中构建 items 数组会使代码变慢,因为新数组被实例化。