传播语法ES6

时间:2016-01-01 20:48:46

标签: javascript ecmascript-6

考虑以下示例代码

var x = ["a", "b", "c"];
var z = ["p", "q"];

var d = [...x, ...z];

var e = x.concat(z);

此处,de的值完全相同且等于["a", "b", "c", "p", "q"],因此,

  1. 这两者究竟有什么区别?
  2. 哪一个更有效率,为什么?
  3. 传播语法究竟有什么用?
  4. 你认为用正式的广泛语言引入这些小捷径可能会留下一些未被注意的错误,我的意思是要么是非常不必要的,要么我没有意识到它的正确需要。

5 个答案:

答案 0 :(得分:18)

  1. 在您给出的示例中,两者之间基本没有区别
  2. .concatsignificantly more efficient: http://jsperf.com/spread-into-array-vs-concat,因为...(spread)仅仅是更基本的基础语法之上的语法糖,它明确地迭代索引以扩展数组。
  3. Spread允许在更笨重的直接数组操作之上使用含糖的语法
  4. 为了扩展上面的#3,你对传播的使用是一个有点人为的例子(尽管可能会经常出现在野外)。例如,当函数体中的整个参数列表传递给.call时,Spread非常有用。

    function myFunc(){
        otherFunc.call( myObj, ...args );
    }
    

    function myFunc(){
        otherFunc.call( myObj, args[0], args[1], args[2], args[3], args[4] );
    }
    

    这是另一个任意的例子,但是为什么扩展运算符在一些其他详细和笨重的情况下使用会更好一点。

    作为@loganfsmyth points out

      

    Spread也适用于任意可迭代对象,这意味着它不仅适用于Array,还适用于MapSet等。

    这是一个很好的观点,并且增加了这样的想法 - 虽然在ES5中实现并非不可能 - 但扩展运算符中引入的功能是新语法中更有用的项目之一。

    对于此特定上下文中扩展运算符的实际基础语法(因为...也可以是“rest”参数),请参阅the specification。 “如上所述,显式迭代索引以扩展数组的更基本的基础语法”足以说明问题,但实际定义使用GetValueGetIterator作为后面的变量。< / p>

答案 1 :(得分:2)

此示例的输出是相同的,但在幕后行为不同,

考虑(检查浏览器的控制台):

var x = [], y = [];

x[1] = "a";
y[1] = "b";

var usingSpread = [...x, ...y];
var usingConcat = x.concat(y);

console.log(usingSpread); // [ undefined, "a", undefined, "b"]
console.log(usingConcat); // [ , "a", , "b"] 

console.log(1 in usingSpread); // true
console.log(1 in usingConcat); // false

Array.prototype.concat将在数组中保留empty slots,而Spread将其替换为undefined值。

输入Symbol.iteratorSymbol.isConcatSpreadable

Spread运算符使用@@iterator符号遍历数组和类似数组的对象,例如:

  • Array.prototype
  • TypedArray.prototype
  • String.prototype
  • Map.prototype
  • Set.prototype

(这就是为什么您可以在它们上使用for .. of的原因)

我们可以覆盖默认的iterator符号,以查看spread运算符的行为:

var myIterable = ["a", "b", "c"];
var myIterable2 = ["d", "e", "f"];

myIterable[Symbol.iterator] = function*() {
  yield 1;
  yield 2;
  yield 3;
};

console.log(myIterable[0], myIterable[1], myIterable[2]); // a b c
console.log([...myIterable]); // [1,2,3]

var result = [...myIterable, ...myIterable2];
console.log(result); // [1,2,3,"d","e","f"]

var result2 = myIterable.concat(myIterable2);
console.log(result2); // ["a", "b", "c", "d", "e", "f"]

另一方面,@@isConcatSpreadable

  

布尔值属性,如果为true,则指示应通过Array.prototype.concat将对象展平为其数组元素。

如果设置为false,则Array.concat不会使数组变平:

const alpha = ['a', 'b', 'c'];
const numeric = [1, 2, 3];

let alphaNumeric = alpha.concat(numeric);

// console.log(alphaNumeric);

numeric[Symbol.isConcatSpreadable] = false;

alphaNumeric = alpha.concat(numeric);

// alphaNumeric = [...alpha, ...numeric];
// the above line will output : ["a","b","c",1,2,3]

console.log(JSON.stringify(alphaNumeric)); // ["a","b","c",[1,2,3]]

但是,spread的{​​{1}} behaves differentlynot iterable

Objects
  

它将自己的可枚举属性从提供的对象复制到新对象。

传播算子更快,请检查spread-into-array-vs-concat(至少是Chrome 67)

然后检查how three dots changed javascript的一些用例,其中包括Destructuring assignment(Array或Object):

var obj = {'key1': 'value1'};
var array = [...obj]; // TypeError: obj is not iterable
var objCopy = {...obj}; // copy

并将字符串拆分为字符数组:

const arr = [1, 2, 3, 4, 5, 6, 7];

const [first, , third, ...rest] = arr;

console.log({ first, third, rest });

答案 2 :(得分:2)

让问题无序,让我们从一个基本问题开始:扩展语法的确切用途是什么?

Spread语法基本上将可迭代元素的内容解包,例如数组或对象。或者,有关MDN Web Docs on spread syntax的更详细说明:

  

Spread语法允许迭代,例如数组表达式或字符串   扩展到零个或多个参数的地方(对于函数   调用)或元素(用于数组文字)或对象   将表达式扩展到零个或多个键值对的地方   (用于对象文字)。

以下是一些典型的扩展语法用例的简单示例,以及一个扩展语法与rest参数之间的区别的示例(它们看起来相同,但执行的功能却几乎相反)。

函数调用:

const multiArgs = (one, two) => {
   console.log(one, two);
}

let args = [1, 2];
multiArgs(...args);
// 1 2

数组或字符串文字:

let arr1 = [2, 3];
let arr2 = [1, ...arr1, 4];
console.log(arr2);
// [1, 2, 3, 4]

let s = 'split';
console.log(...s);
// s p l i t

对象文字:

let obj1 = { 1: 'one' };
let obj2 = { 2: 'two' };
let obj3 = { ...obj1, ...obj2 };
console.log(obj3);
// { 1: 'one', 2: 'two' }

其余参数语法与扩展语法不同:

Rest parameter语法看起来与扩展语法相同,但是实际上表示未知数量的函数参数为数组。因此,与其“拆包”可迭代的rest参数,实际上是将多个参数打包到一个数组中。

const multiArgs = (...args) => {
   console.log(args);
}

multiArgs('a', 'b', 'c');
// ['a', 'b', 'c']

扩展语法性能/效率:

要解决与其他方法相比的效率问题,唯一的诚实答案是“取决于”。浏览器一直在变化,与特定功能关联的上下文和数据会产生截然不同的性能结果,因此您会发现各种冲突的性能时序,这表明传播语法比您可能使用的各种数组或对象方法快得惊人得多,而且荒谬得多用于完成类似的目标。最后,对于速度优化至关重要的任何情况,都应该进行对比测试,而不是依赖那些忽略代码和数据细节的简单函数的通用时序。

concat()的比较:

最后,快速讨论一下问题代码中显示的扩展语法和concat()之间的区别。区别在于,传播语法不仅可以用于连接数组,还可以使用更多的语法,但是concat()在IE等较旧的浏览器中也可以使用。如果您不担心与旧版浏览器的兼容性,并且无需进行速度微优化,那么在扩展语法和concat()之间进行选择只是您发现可读性更高的问题:arr3 = arr1.concat(arr2)arr3 = [...arr1, ...arr2]

答案 3 :(得分:0)

在给定的例子中,这两者之间没有区别。对于连接,我们可以使用concat方法而不是spread运算符。但是,扩展运算符的使用不限于数组的串联。

扩展语法允许扩展诸如数组表达式或字符串之类的可迭代。它可以在以下场景中使用。

  1. 使用数组扩展运算符

    • 阵列连接
    • String to Array
    • Array as as Arguments to function。
  2. 使用对象传播运算符

    • 对象的连接
  3. 要了解如何演示所有这些用途并尝试使用代码,请按照以下链接(codepen.io)

    ES6-Demonstration of Spread Operator

    /**
    * Example-1: Showing How Spread Operator can be used to concat two or more     
    arrays. 
    */
    const americas = ['South America', 'North America'];
    
    const eurasia = ['Europe', 'Asia'];
    
    const world = [...americas, ...eurasia];
    
    /**
    * Example-2: How Spread Operator can be used for string to array.
    */
    const iLiveIn = 'Asia';
    const iLiveIntoArray = [...iLiveIn];
    
    /**
    * Example-3: Using Spread Operator to pass arguments to function
    */
    const numbers = [1,4,5];
    
    const add = function(n1,n2,n3){
    return n1 + n2 + n3;
    };
    
    const addition = add(numbers[0],numbers[1],numbers[2]);
    const additionUsingSpread = add(...numbers);
    
    /**
    * Example-4: Spread Operator, can be used to concat the array
    */
    
    const personalDetails = {
      name: 'Ravi',
      age: '28',
      sex: 'male'
    };
    
    const professionalDetails = {
      occupation: 'Software Engineer',
      workExperience: '4 years'
    };
    
    const completeDetails = {...personalDetails, ...professionalDetails};
    

答案 4 :(得分:0)

const colors = ['Blue','Red','Black']; // 简单数组。

const my_colours = ['Blue','Red','Black','Yellow','Green'];

const favourite_colours = [...my_colours,'grey']; //[...] 在另一个数组中展开运算符访问数据。