解构赋值表达式和 ... 操作符

Created
Feb 27, 2021 04:09 AM
Tags
javascript
 
解构赋值和部分...运算符是 ES6 带来的新语法糖, 有了他们, 平时看似整洁的代码瞬间也变得冗余了, 快来尝试一下这两个新语法吧
词条列表(点击前往相关文档)

解构赋值的原理及语法

我的理解: 解构都是对标准对象的解构
标准对象: 包含普通对象和特异对象(Array String object 等)
notion image
如图, 解构赋值的格式可以通写为:
let {matchedKey: varName} = {key: balue}
matchedKey : 与=右边对象匹配的键varName : 使用 let 声明的变量名 实际栗子,对象中函数访问对象键值
const chart = {
  price: 1000,
  name: 'sorb',
  isNew: true,
  getComputedPrice() {
    // 对this结构赋值,方便函数操作
    let { price, name, isNew } = this
    isNew && (price *= 0.8)
    return price
  },
}
看似依旧有点复杂, 并不比单行单行来得轻松多少,好在可以简写
第一种简写: 把varName 默认作为matchedKey, 只写matchedKey
let { name } = obj
第二种简写: 数组或类数组[甚至说一切可遍历的数据结构] , (从对象的角度说)不用设置属性名, 直接写出变量名 => 语法格式为:
let [varName, varName2] = ary;
由于此类数据结构的可遍历性, 也就是说每个成员的顺序是可以确定的, 不必匹配键名我们也可以达到一一对应的效果
此外, 正因为它是有顺序[或者说有索引]存在的标准对象, 所以使用 [] 来包围 varName 就有理可循了
到这里有些小甜菜可能有会疑问了?
误解=> 这种简写之所以使用[] ,是因为=右边是数组, 两边格式需要匹配
纠正=> 使用两张图反对这种观点:
  • {}[]并不一定需要匹配
notion image
  • 下图再次证明有索引就适合用[]表示
notion image
不过, 大可不必过于纠结, 毕竟有了简写, 谁会像上图一样垒字呢 😋
第二种简写的延申: 类比第二种简写方式, 我们可以利用等号左右一一对应来对多个变量赋值, 语法格式为:
let [name, sex, age] = ['beet', 'unknown', 99]
这也是最常见的解构赋值, 通过一行代码, 我们同时声明了name sex age 三个变量, 非常简单易用
扩展: 解构不到值?
如果结构赋值语句不能与=右边标准对象匹配, 变量值为undefined
一个极简单的 🌰:
notion image
我们再次对变量 d 进行操作, 极有可能会报错, 以至下文代码需要条件判断 d 的值, 不必烦恼, 解构赋值可以让我们提前判断: 使用默认值 , 其语法就是在左边解构赋值语句种给变量赋予默认初始值
let [a, b, unmatchedKey = 4] = [2, 3]
当解构失败, 变量值不再是undefined, 而是我们设置的默认值
把上面代码优化一下:
notion image
所以, 解构时的默认值可以替代两个语句: 判断值是否 undefined => 是 undefined 赋予初始值, 这在函数形参赋值中极为好用, 我们继续往下看!
扩展: 函数形参的解构赋值
函数形参是函数作用域内的变量, 所以同样可以解构赋值并设置默认值, 根据形参赋值的原理, 我们可以想象成如下语法格式
let [parameter1, parameter2] = [argument1, argument2]
// parameter => 形参
// argument  => 实参
把此表达式抽象到函数, 我们可以猜到对形参进行解构赋值的语法了
let foo = ([parameter1, parameter2]) => {
  console.log(`${parameter1 + parameter2}`)
}
foo([1, 2])
notion image
配合...操作符, 解构赋值还有更强的功能( 详见:...操作符 )

...操作符

对于可遍历的数据结构, 我们可以使用... 快速实现  两种效果

 => 展开语法

1.可将可遍历的结构[把其中的每一项内容称作子项]=>展开为以逗号分隔的各个子项
console.log(...'str') // s t r
2.还可以在构造字面量对象时, 将对象表达式按 key-value 的方式展开
let cloneObj = { ...obj }
克隆数组和对象
let ary = [1, 2, 3],
  cloneAry = [...ary]
console.log(ary, cloneAry)
// Array(3) [ 1, 2, 3 ] Array(3) [ 1, 2, 3 ]

let obj = {
    one: 1,
    two: 2,
  },
  cloneObj = { ...obj }
console.log(obj, cloneObj)
// Object { one: 1, two: 2 } Object { one: 1, two: 2 }
注意:cloneAry是一个新的数组, 指向一个新的堆内存地址, cloneAry 与 ary互不影响, 同理, cloneObj与原对象也不影响
数组方法的实现
concat => array = [...array, ...ary];
unshift => ary = ['unshift item', ...ary];
push => ary = [...ary, 'push item'];
join(',') => ary = `${[...ary]}`;
实例:
let array = ['a', 'b'],
  ary = [1, 2]
array = [...array, ...ary] //concat
ary = [0, ...ary] // unshift
ary = [...ary, 3] // push
ary = `${[...ary]}` // join(',')
notion image
函数传参(数组)与返回
传参数组 : 我们可以利用展开语法替换 Function.prototype.apply()方法
let foo = () => console.log()
foo(...ary) === foo.apply(...ary) // => true
数组返回结果的展开输出 : 当函数返回结果包含多项的时候, 我们常常把他们放在一个数组中用于返回输出, ...[] 可以更加直观地输出
let foo = (a, b, c) => [a, b, c]
console.log(...foo(1, 2, 3)) // 1 2 3
配合结构赋值, 把输出的结果赋予各个变量
let foo = (a, b, c) => [a, b, c]
let [one, two, three] = [...foo(1, 2, 3)]
console.log(one, two, three) // 1 2 3

 => 剩余语法

可将有序不定数量的参数按序拼成一个数组 (函数中的剩余语法是把实参按需拼成数组, 如果没有传参 => 拼成空数组)
语法: let [...ary] = [1,2,3]
ary为变量名
注意: 如果函数体有对这个数组的操作, 报错
notion image
let foo = (...ary) => {
  console.log(ary)
  return ary.reduce((plus, index) => plus + index)
}
foo(1, 2, 3) // Array(3) [ 1, 2, 3 ] 6
数组方法的实现
注意: 由于...剩余运算符只能放在解构赋值语句的末尾, 所以操作数组的方法有限, 实际用处不大
shift => [, ...ary] = ary;
multi-shift => [,,, ...ary] = ary;
把类数组转换为数组
类数组: HTMLCollection Nodelist arguments
notion image
用剩余语法转换成数组:
notion image
借此我们可以替换掉Array.prototype.slice.call(list) 或Array.from(list)
函数传参接收
函数普通的传参(不传数组)可以使用剩余运算符接收传进来的实参
语法: let foo = (...ary) => console.log(ary);
当然, ary 位置也可以变为解构赋值语句
;(foo = (...[a, b, c]) => {
  console.log(a, b, c)
})(1, 2, 3) // 1 2 3

箭头函数取代 arguments
除了普通函数可以用剩余运算符
当然, 还有更多的功能等待我们去尝试, 就叨叨到这儿, 大家快去实操把 🤘