ES6-解构赋值
解构赋值(Destructuring Assignment)是一个在ES6的新特性,用于提取(extract)阵列或物件中的资料,这是一种对原本语法在使用上的改进,过去要作这件事可能需要使用回圈或迭代的语句才行,新语法可以让程式码在撰写时更为简短与提高阅读性。
解构赋值的解说只有一小段英文:
The destructuring assignment syntax is a JavaScript expression that makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals.
这句后面的mirrors the construction of array and object literals,代表这个语法的使用方式- 如同"镜子"一般,对映出阵列或物件字面的结构。也就是一种样式(pattern)对映的语法。
解构赋值如果你能抓得住它的基本概念,就可以很容易理解与使用。不过与ES6其他的特性配合时,会显得复杂比较难理解。
在使用时有以下几种常见的情况:
- 从阵列解构赋值
- 从物件解构赋值(或是从混用物件或阵列)
- 非物件或非阵列解构赋值
- 解构赋值时给定预设值
- 搭配函式的传入参数使用
destructuring: 变性、破坏性。使用"解构"是对照de-字头有"脱离"、"去除"的意思。assignment: 赋值、指派。赋值通常指的是程式中使用等号(=)运算符的语句。
从阵列解构赋值(Array destructuring)
从阵列解构赋值没太多学问,唯一比较特别的是可以用其余运算符(Rest Operator)的语法,既然是其余运算符,最后就会把其余的对应值集合成一个阵列之中。下面是几个几个范例:
//基本用法
const [a, b] = [1, 2] //a=1, b=2
//先宣告後指定值,要用let才行
let a, b
[a, b] = [1, 2]
// 略過某些值
const [a, , b] = [1, 2, 3] // a=1, b=3
// 其餘運算
const [a, ...b] = [1, 2, 3] //a=1, b=[2,3]
// 失敗保護
const [, , , a, b] = [1, 2, 3] // a=undefined, b=undefined
// 交換值
const a = 1, b = 2;
[b, a] = [a, b] //a=2, b=1
// 多維複雜陣列
const [a, [b, [c, d]]] = [1, [2, [[[3, 4], 5], 6]]]
// 字串
const str = "hello";
const [a, b, c, d, e] = str
用法就是这么简单,用来赋值的等号符号(=)左边按照你写的变数或常数样式,然后在右边写上要对映数值,就像之前说的"镜子"般的对应。当没有对应的值时,就会得到undefined。
从物件解构赋值(Object destructuring)
物件除了有使用特别的字面符号,也就是花括号({})来定义,其中也会包含属性。按照基本的原则,也是用像"镜子"般的样式对应,一样看范例就很容易理解:
// 基本用法
const { user: x } = { user: 5 } // x=5
// 失敗保護(Fail-safe)
const { user: x } = { user2: 5 } //x=undefined
// 賦予新的變數名稱
const { prop: x, prop2: y } = { prop: 5, prop2: 10 } // x=5, y=10
// 屬性賦值語法
const { prop: prop, prop2: prop2 } = { prop: 5, prop2: 10 } //prop = 5, prop2=10
// 相當於上一行的簡短語法(Short-hand syntax)
const { prop, prop2 } = { prop: 5, prop2: 10 } //prop = 5, prop2=10
// ES7+的物件屬性其餘運算符
const {a, b, ...rest} = {a:1, b:2, c:3, d:4} //a=1, b=2, rest={c:3, d:4}
下面的语法是个有陷阱的语法,这是错误的示范:
// 錯誤的示範:
let a, b
{ a, b } = {a: 1, b: 2}
注: 这个语法如果使用const来宣告常数是根本不能使用,只能用let来宣告变数。而且eslint检查工具一定会回报语法错误。
因为在Javascript语言中,虽然使用花括号符号({})是物件的宣告符号,但这个符号用在程式叙述中,也就是前面没有let、const、var这些宣告字词时,则是代表程式码的区块(block)。在外面再加上括号符号(())就可以改正,括号符号(())有表达式运算的功能,正确的写法如下:
let a, b
({ a, b } = {a: 1, b: 2}) //a=1, b=2
注: 在大部份情况,你应该是在定义常数或变数时,就进行解构赋值。
复杂的物件或混合阵列到物件,如果你能记住之前说的镜子样式对映基本原则,其实也很容易就能理解:
// 混用物件與陣列
const {prop: x, prop2: [, y]} = {prop: 5, prop2: [10, 100]}
console.log(x, y) // => 5 100
// 複雜多層次的物件
const {
prop: x,
prop2: {
prop2: {
nested: [ , , b]
}
}
} = { prop: "Hello", prop2: { prop2: { nested: ["a", "b", "c"]}}}
console.log(x, b) // => Hello c
从非阵列或非物件解构赋值
从其他的资料类型进行阵列或物件解构,一开始是null或undefined这两种时,你会得到错误:
const [a] = undefined
const {b} = null
//TypeError: Invalid attempt to destructure non-iterable instance
如果是从其他的原始资料类型布林、数字、字串等作物件解构,则会得到undefined值。
const {a} = false
const {b} = 10
const {c} = 'hello'
console.log(a, b, c) // undefined undefined undefined
从其他的原始资料类型布林、数字、字串等作阵列解构的话,只有字串类型可以解构出字元,在上面的例子有看到这种情况,其他也是得到undefined值:
const [a] = false
const [b] = 10
const [c] = 'hello' //c="h"
console.log( a, b, c)
注: 字串资料类型的值只能用在阵列的解构赋值,无法用在物件的解构赋值。
以上会有出现这样的结果,是当一个值要被进行解构时,它会先被转成物件(或阵列),因为null或undefined无法转成物件(或阵列),所以必定产生错误,这是第一阶段。下一个阶段如果这个值转换的物件(或阵列),没有附带对应的迭代器(Iterator)就无法被成功解构赋值,所以最后回传undefined。
解构赋值时的预设值
在等号左边的样式(pattern)中是可以给定预设值的,作为如果没有赋到值时(对应的值不存在)的预设数值。
const [missing = true] = []
console.log(missing)
// true
const { message: msg = 'Something went wrong' } = {}
console.log(msg)
// Something went wrong
const { x = 3 } = {}
console.log(x)
// 3
要作一个简单的陷阱题满简单的,你可以试看看下面这个范例中到底是赋到了什么值:
const { a ='hello' } = 'hello'
const [ b ='hello' ] = 'hello'
console.log( a, b)
在函式传入参数定义中使用
在函式传入参数定义中也可以使用解构赋值,因为函式的传入参数本身也有自己的预设值设定语法,这也是ES6的一个特性,所以使用上会容易与解构赋值自己的预设值设定搞混。这地方会产生不少陷阱。
一个简单的解构赋值用在函式的参数里,这是正常情况的语法:
function func({a, b}) {
return a + b
}
func({a: 1, b: 2}) // 3
当你用上了预设值的机制,而且前面的a有预设值,后面的b就没有,这时候因为没有赋到值时,都会是undefined值,任何数字加上undefined都会变成NaN,也就是非数字的意思:
function func({a = 3, b}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({b: 2}) // 5
func({a: 1}) // NaN
func({}) // NaN
func() // Cannot read property 'a' of undefined
当a与b两个都有预设值时,NaN的情况不存在:
function func({a = 3, b = 5}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // Cannot read property 'a' of undefined
实际上函式传入参数它自己也可以加预设值,但这情况会让最后一种func()呼叫时与func({})相同结果:
function func({a = 3, b = 5} = {}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // 8
另一种情况是在函式传入参数的预设值中给了另一套预设值,这只会在func()时发挥它的作用:
function func({a = 3, b = 5} = {a: 7, b: 11}) {
return a + b
}
func({a: 1, b: 2}) // 3
func({a: 1}) // 6
func({b: 2}) // 5
func({}) // 8
func() // 18
你可以观察一下,当对某个变数赋值时你给他null或void 0,到底是用预设值还是没有值,这个范例的g()函式是个对照组:
function func({a = 1, b = 2} = {a: 1, b: 2}) {
return a + b
}
func({a: 3, b: 5}) // 8
func({a: 3}) // 5
func({b: 5}) // 6
func({a: null}) // 2
func({b: null}) // 1
func({a: void 0}) // 3
func({b: void 0}) // 3
func({}) // 3
func() // 3
function g(a = 1, b = 2) {
return a + b
}
g(3, 5) // 8
g(3) // 5
g(5) // 7
g(void 0, 5) // 6
g(null, 5) // 5
g() // 3
注:所以在函式传入参数中作解构赋值时,给定null值时会导致预设值无用,请记住这一点。当数字运算时,null相当于0。
实例应用
迭代物件中的属性值
这个范例用了for...of语法。出自Destructuring assignment:
const people = [
{
name: 'Mike Smith',
family: {
mother: 'Jane Smith',
father: 'Harry Smith',
sister: 'Samantha Smith'
},
age: 35
},
{
name: 'Tom Jones',
family: {
mother: 'Norah Jones',
father: 'Richard Jones',
brother: 'Howard Jones'
},
age: 25
}
];
for (let {name: n, family: { father: f } } of people) {
console.log('Name: ' + n + ', Father: ' + f)
}
// "Name: Mike Smith, Father: Harry Smith"
// "Name: Tom Jones, Father: Richard Jones"
结合预设值与其余参数
这个范例混用了一些ES6的语法,出自Several demos and usages for ES6 destructuring.:
// 結合其他ES6特性
const ajax = function ({ url = 'localhost', port: p = 80}, ...data) {
console.log('Url:', url, 'Port:', p, 'Rest:', data)
}
ajax({ url: 'someHost' }, 'additional', 'data', 'hello')
// => Url: someHost Port: 80 Rest: [ 'additional', 'data', 'hello' ]
ajax({ }, 'additional', 'data', 'hello')
// => Url: localhost Port: 80 Rest: [ 'additional', 'data', 'hello' ]
_.pluck
这个例子相当于Underscore.js函式库中的_.pluck,把深层的属性值往上拉出来。
var users = [
{ user: "Name1" },
{ user: "Name2" },
{ user: "Name2" },
{ user: "Name3" }
]
var names = users.map( ({ user }) => user )
console.log(names)
// => [ 'Name1', 'Name2', 'Name2', 'Name3' ]
react Native的解构赋值
这个是React Native的一个教学,里面有用了解构赋值的语法。出自React Native Tutorial: Building Apps with JavaScript:
var React = require('react-native')
var {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component
} = React本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!