深入了解扩展运算符

2,882 阅读4分钟

扩展运算符作用

扩展运算符允许一个表达式在期望多个参数(用于函数调用)或多个元素(用于数组字面量)或多个变量(用于解构赋值)的位置扩展。

例子

  1. 例子一:

     let a = [1,2,3,4,5,7,3]
     console.log(a);    //[ 1, 2, 3, 4, 5, 7, 3 ]
     console.log(...a);    // 1, 2, 3, 4, 5, 7, 3 
    

等同于

    console.log(1,2,3,4,5,7,3);    // 1, 2, 3, 4, 5, 7, 3 

也就是说扩展运算符帮我们把这个数组在这里帮我们展开了,再看一个例子

  1. 例子二:

     let a = [1,2,3]
     let b = [1,4]
     let c = [...a,...b] 
     console.log(c);    // [1,2,3,1,4]
    

等同于

    let c = [1,2,3,1,4]

可以使用扩展运算符复制,拼接数组

    let a = [1,2,3]
    let b = [2,4]
    let c = [...a]
    let d = [...a,...b,...c]

再来看对象的扩展运算符使用,对象也是有子元素的,也可以使用扩展运算符扩展

例子一

    let obj1 = {
        "name" : "cys",
        "age" : 18,
        "sex" : "man"
    }
    
    let obj2 = {
        "name" : "cysg",
        "age" : 19
    }
    
    let obj3 = {
        ...obj1,
        ...obj2
    }

    console.log(obj3); //{ name: 'cysg', age: 19, sex: 'man' }

obj,obj2换个位置再来看

    let obj3 = {
        ...obj2,
        ...obj1
    }

    console.log(obj3); //{ name: 'cys', age: 18, sex: 'man' }

从返回的值可以看到,obj3得到了obj1和obj2的属性,且在后面的会覆盖掉前面同一属性的值,非常简单的实现了浅复制,在替换对象属性也非常方便

    (options)=>{
        this.options = {...this.options,...options}
    }

扩展运算符把对象的子元素进行位置扩展,如果你想把对象的元素都拿出来(单个拿出来可不行),就可以使用扩展运算符,那么拿出来给谁,怎么使用就要好好说道说道了 我们先来尝试一下数组转对象

  1. 数组扩展为对象

     let a = [1,2,3]
     let b = [2,4]
     
     let c = {
         ...a,
         ...b
     }
     
     console.log(c);   // { '0': 2, '1': 4, '2': 3 }
    

扩展运算符根据数组的下标和值作为key:value键值对插入对象,同对象使用扩展运算符一样没有任何问题,那么对象转化为数组会发生什么呢?

  1. 对象扩展为数组

     let obj1 = {
         "name" : "cys",
         "age" : 18,
         "sex" : "man"
     }
     
     let obj2 = {
         "name" : "cysg",
         "age" : 19
     }
     
     let arr = [...obj1,...obj2]
     
     console.log(arr);
    

报错了!提示我们obj1不是iterable,obj1是不可迭代的,那我们能输出...obj1吗

    console.log(...obj1);

也不行,报了相同的错,对象可以扩展为对象,数组也可以扩展为对象,那对象转数组为什么会出错呢?根据提示,我们尝试给这个对象定义一个iterator

let obj1 = {
    "name": "cys",
    "age": 18,
    "sex": "man",
    [Symbol.iterator]: function () {
        const self = this;
        let keys = Object.keys(obj1)
        let index = 0;
        let len = keys.length
        return {
            next: function () { //实现next
                if (index < len) {
                    return { //遍历中
                        value: self[keys[index++]],
                        done: false //表示遍历没有结束,done设置为fasle
                    }
                } else {
                    return { //遍历结束
                        value: undefined, //结束后,返回undefined
                        done: true //done设置为true,表示遍历结束
                    }
                }
            }
        }
    }
}

再输出看看

console.log(...obj1); // cys 18 man
console.log([...obj1]); // [ 'cys', 18, 'man' ]

再看

let obj1 = {
    "name": "cys",
    "age": 18,
    "sex": "man",
    [Symbol.iterator]: function () {
        const self = this;
        let keys = Object.keys(obj1)
        let index = 0;
        let len = keys.length
        return {
            next: function () { //实现next
                if (index < len) {
                    return { //遍历中
                        value: self[keys[index++]] + 'hello',
                        done: false //表示遍历没有结束,done设置为fasle
                    }
                } else {
                    return { //遍历结束
                        value: undefined, //结束后,返回undefined
                        done: true //done设置为true,表示遍历结束
                    }
                }
            }
        }
    }
}

再输出看看

console.log(...obj1); // cyshello 18hello manhello
console.log([...obj1]); // [ 'cyshello', '18hello', 'manhello' ]
console.log({...obj1}); 
    //  { name: 'cys',
    //  age: 18,
    //  sex: 'man',
    //  [Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]] }

可以看到,对象转对象并没有发生改变,只是把元素都扩展了出来,而当要转化为一个可迭代的数据结构时,就会调用自身的迭代器方法,由此可以得出结论当扩展运算符要扩展为可迭代的对象时,是通过iterator进行扩展的,扩展出来的值是通过iterator.next()得到的,如果修改next()返回的值的顺序,那也会改变扩展运算符扩展出来的顺序。相同类型的对象扩展只是对元素的位置进行扩展

基本数据类型能否使用扩展运算符?

现在我们只讨论了复杂数据类型,对象,那么基础数据类型能否使用扩展运算符呢?

让我们来试试

  • string

      console.log(...'asdwr'); //a s d w r
    

string 类型是一个伪数组,有iterator,可以使用扩展运算符

  • number

      console.log(...1);
    

报错,number不能使用扩展运算符

  • boolean

      console.log(...true);
    

报错,boolean不能使用扩展运算符

  • null

      console.log(...null);
    

报错,没有iterator这个属性

  • undefined

      console.log(...undefined);
    

报null一样的错

  • 如果把基础数据类型用扩展运算符放在对象里,会不会有什么不一样的事情发生呢? 来看

      let a = {
          ...null
      }
      console.log(a);  // {}
    

经过测试,都不会报错,但是扩展出来的都是一个空对象。

结论

扩展运算符可以广泛用于对象的浅复制和多个对象的数据拼接,当要转化为可迭代数据结构时可设置对象的迭代器对扩展运算符扩展出来的值进行操作,基础数据只有string可以使用扩展运算符,number,boolean,null,undefined无效

如果有错,请指正,相互学习