toFrankie / blog

种一棵树,最好的时间是十年前。其次,是现在。
20 stars 1 forks source link

解构赋值被滥用? #318

Open toFrankie opened 1 year ago

toFrankie commented 1 year ago

配图源自 Freepik

开始

某天看到这样一个老帖子调侃道:

// let inputClass = manager.options.inputClass
let { options: { inputClass } } = manager

不可否认,ES6 的解构赋值(Destructuring Assignment)语法在日常开发中带来了极大的便利。但是...

看起来不错

有一个对象,

var store = {
  name: 'Apple Store Parc Central',
  national_code: '86',
  phone: '4006139742'
}

若要将其属性值赋予新变量,在 ES5 及以前,

var name = store.name
var phone = store.phone

在 ES6 及以后,

const { name, phone } = store
const { national_code: nationalCode } = store

到目前为止,看起来很不错。

看起来还行

若是一个嵌套类型的对象,

const store = {
  name: 'Apple Store Parc Central',
  national_code: '86',
  phone: '4006139742',
  address: {
    province: 'Guangdong',
    city: 'Guangzhou',
    county: 'Tianhe',
    street: 'No. 218, Tianhe Road',
  }
}

一部分同学可能会这样用,

const { address: { province, city } } = store

而我个人更偏向于,

const { province, city } = store.address

为什么呢?

假设数据源 store 来自于别处,其中 store.address 可能是一个不存在的属性,为避免解构出错,我们可能会为其设置默认值。比如:

const { address: { province, city } = {} } = store

const { province, city } = store.address || {}

个人认为后者更直观一些,这也是我选择它的原因之一。

前者还有个陷进,如果 store.addressnull,默认值就不生效,自然解构也会出错。

但如果要获取不同层级的属性时,也会像前面那样用,

const { name, address: { province, city } } = store

看起来不好!

若对象时一个嵌套多层的对象,

const store = {
  name: 'Apple Store Parc Central',
  national_code: '86',
  phone: '4006139742',
  address: {
    province: 'Guangdong',
    city: 'Guangzhou',
    county: 'Tianhe',
    street: 'No. 218, Tianhe Road',
  },
  activity: {
    today: {
      name: '光影技巧:用 iPhone 拍摄照片',
      description: '用 iPhone 拍出更棒的照片和视频。',
      start_at: '1690090200',
      end_at: '1690093800',
    },
  },
}

就可能会出现这种令人抓狂的写法,

const {
  activity: {
    today: { start_at: startAt },
  },
} = store

// or
const { activity: { today: { start_at: startAt } = {} } = {} } = store

给人的感觉是「为了用而用」。

为什么不用最原始的方式?忘本了?

const startAt = store.activity.today.start_at

即使这样,可读性也比前面的好吧,

const { start_at: startAt } = store.activity.today

新语法固然是为了解决某种场景而生的,但其并非适用于所有场景的啊。有句话怎么说来着:「因地制宜」。

那应该怎么写?

解构赋值语法允许指定默认值。但当属性值为 null 时,并不会赋予默认值。假设以下 store.activitynull 时,解构会出错,

const { activity: { today: { start_at: startAt } = {} } = {} } = store

我们可能还是要这样写,

const startAt = store && store.activity && store.activity.today ? store.activity.today.start_at : undefined

在这篇文章有一种写法,

const getPropValue = (obj, key) => {
  return key.split('.').reduce((o, x) => (o == undefined ? o : o[x]), obj)
}

const startAt = getPropValue(store, 'activity.today.start_at') // "1690090200"

对于获取深层属性的情况,很多工具库都有提供现成的方法,感兴趣可看下实现。比如:

后来,ES2020 增加了可选链(Optional Chains)语法 ?.,就可以这样去写了:

const { province, city } = store?.address || {}

const startAt = store?.activity?.today?.start_at

当某个属性值为空值(nullishundefined or null),则短路返回 undefined

最后

解构赋值虽好,但不要贪杯哦!