学习使用JavaScript去结构化编写更简洁的代码

69 阅读6分钟

解构是我最喜欢的JavaScript工具之一,简单来说,解构允许你将一个复杂的结构(如数组或对象)分解成更简单的部分,尽管它不仅仅是这样。

让我们在一个例子中更好地看到它。

const article = {
  title: "My Article",
  rating: 5,
  author: {
    name: "Juan",
    twitter: "@bajcmartinez"
  }
}

// Now lets print it to the screen
console.log(`"${article.title}" by ${article.author.name} had ${article.rating} stars`)

// But using destructuring, we can achieve the same by doing
const { title, rating, author: { name } } = article
console.log(`"${title}" by ${name} had ${rating} stars`)

------------------------
Output
------------------------
"My Article" by Juan had 5 stars
"My Article" by Juan had 5 stars

现在,有些人已经使用这个功能有一段时间了,也许是白色的构建React应用程序,但他们并不完全理解它,对其他人来说,这可能是第一次。所以我将从一开始就引导你,以便在文章结束时我们都有相同的理解水平。


解构对象

在上面的例子中,所有的魔法都发生在下面一行。

const { title, rating, author: { name } } = article

现在看来,在赋值的左边有这样的括号有点奇怪,但这就是我们告诉JavaScript我们正在解构一个对象的方式。

对对象进行析构,可以让你在任何深度上绑定对象的不同属性。让我们从一个更简单的例子开始。

const me = {
  name: "Juan"
}

const { name } = me

在上面的例子中,我们声明了一个叫做name 的变量,它将从对象me 中的同名属性中初始化出来,所以当我们评估name 的值时,我们得到Juan 。真棒!这也可以应用于任何深度的对象。这同样可以应用于任何深度,回到我们的例子中来。

const { title, rating, author: { name } } = article

对于titlerating ,这和我们已经解释过的完全一样,但是在author ,事情有点不同。当我们到达一个对象或数组的属性时,我们可以选择是否用对article.author 对象的引用来创建一个变量author ,或者我们可以做一个深层次的解构,以获得对内部对象属性的直接访问。

  • 访问对象属性
const { author } = article
console.log(author.name)

------------------------
Output
------------------------
Juan
  • 做一个深度或嵌套的解构
const { author: { name } } = article

console.log(name)
console.log(author)

------------------------
Output
------------------------
Juan
Uncaught ReferenceError: author is not defined

等等,什么?如果我解构了author ,为什么它没有被定义?其实事情很简单,当我们要求JavaScript同时析构author 对象时,这个绑定本身并没有被创建,相反,我们可以访问我们选择的所有author 的属性。所以请永远记住这一点。

  • 展开运算符(......)
const article = {
  title: "My Article",
  rating: 5,
  author: {
    name: "Juan",
    twitter: "@bajcmartinez"

const { title, ...others } = article

console.log(title)
console.log(others)

------------------------
Output
------------------------
My Article
> {rating: 5, author: {name: "Juan", twitter: "@bajcmartinez" }}

此外,我们可以使用传播操作符... 来创建一个具有所有没有被析构的属性的对象。

重命名属性

解构的一个很好的属性是可以为变量选择一个与我们要提取的属性不同的名字。让我们看一下下面的例子。

const me = { name: "Juan" }
const { name: myName } = me

console.log(myName)

------------------------
Output
------------------------
Juan

通过在一个属性上使用: ,我们可以为它提供一个新的名字,在我们的例子中是newName 。然后我们就可以在我们的代码中访问这个变量。需要注意的是,一个具有原始属性的变量name ,在我们的例子中,名字不会被定义。

缺少的属性

那么,如果我们试图解构一个没有在我们对象中定义的属性,会发生什么呢?

const { missing } = {}
console.log(missing)

------------------------
Output
------------------------
undefined

在这种情况下,变量被创建,其值为undefined

默认值

扩展到缺失的属性,当属性不存在时,可以为其分配一个默认值,让我们看看一些例子。

const { missing = "missing default" } = {}
const { someUndefined = "undefined default" } = { someUndefined: undefined }
const { someNull = "null default" } = { someNull: null }
const { someString = "undefined default" } = { someString: "some string here" }

console.log(missing)
console.log(someUndefined)
console.log(someNull)

------------------------
Output
------------------------
missing default
undefined default
null
some string here

在上面的例子中,我们演示了一些为我们的破坏分配默认值的例子。只有当属性是undefined ,才会分配默认值。如果属性的值是null ,或者是string ,则不会分配默认值,而是属性的实际值。


解构数组和迭代表

我们已经看到了一些重构对象的例子,但同样也可以适用于一般的数组或迭代表。让我们从一个例子开始

const arr = [1, 2, 3]
const [a, b] = arr
console.log(a)
console.log(b)

------------------------
Output
------------------------
1
2

这个例子是不言自明的,当我们需要解构一个数组时,我们需要使用[] ,而不是{} ,并且我们可以用不同的变量来映射数组的每个位置。但也有一些不错的技巧

跳过元素

通过使用, 操作符,我们可以从迭代器中跳过一些元素,如下所示。

const arr = [1, 2, 3]
const [a,, b] = arr
console.log(a)
console.log(b)

------------------------
Output
------------------------
1
3

不是说在, 之间留空就能跳过元素,这很微妙,但对结果有很大影响。

我还能做什么?你也可以使用扩散运算符... ,如下所示。

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const [a,, b, ...z] = arr
console.log(a)
console.log(b)
console.log(z)

------------------------
Output
------------------------
1
3
(7) [4, 5, 6, 7, 8, 9, 10]

在这种情况下,z 将获得所有在b 之后的值作为一个数组。或者你有更具体的需求,你想破坏数组中的特定位置,没问题,JavaScript帮你搞定。

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
const { 4: fourth, 9: ninth } = arr
console.log(fourth)
console.log(ninth)

------------------------
Output
------------------------
5
10

如果我们把数组当作一个对象来解构,我们就可以把索引当作属性来使用,从而访问数组中的任何位置。

缺少的属性

和对象的情况一样,我们也可以为数组中未定义的元素设置默认值。让我们看一下一些例子。

const [missing = 'default missing'] = []
const [a, b, c = "missing c", ...others] = [1, 2]

console.log(missing)
console.log(a)
console.log(b)
console.log(c)
console.log(others)

------------------------
Output
------------------------
default missing
1
2
missing c
[]

对于解构数组,也可以为undefined 属性设置默认值,然而,当我们有传播操作符..., ,在undefined ,将返回一个空数组时,就不可能设置默认值。


交换变量

这是解构的一个有趣的用例,2个变量可以在一个单一的表达式中互换。

let a = 1
let b = 5

[a, b] = [b, a]

console.log(a)
console.log(b)

------------------------
Output
------------------------
5
1

使用计算属性的结构化

到目前为止,任何时候我们想解构一个对象的属性,或者一个迭代器的元素,我们都是使用静态键。如果我们想要的是动态键(如存储在变量上的键),我们需要使用计算属性。

这里有一个例子。

const me = { name: "Juan" }
let dynamicVar = 'name'
let { [dynamicVar]: myName } = me

console.log(myName)

------------------------
Output
------------------------
Juan

很棒吧!通过在[] 之间使用一个变量,我们可以在做赋值之前评估它的值,因此可以做动态的Destructuring,尽管必须为这个新变量提供一个名字。


解构函数参数

解构变量可以放在我们可以声明变量的任何地方,例如通过使用let,constvar ,但也可以在函数参数中解构。下面是这个概念的一个简单例子。

const me = { name: "Juan" }

function printName({ name }) {
    console.log(name)
}

printName(me)

------------------------
Output
------------------------
Juan

非常简单和优雅。同样,我们之前讨论的所有规则也适用。


结语

解构在开始时可能看起来很笨拙,但一旦你习惯了,就没有回头路了。它真的可以帮助你的代码更有可读性,是一个值得了解的好概念。