變量的解構賦值
目錄:
-
數組的解構賦值
-
對象的結構賦值
-
字符串的解構賦值
-
數值和布爾值的解構賦值
-
函數參數的解構賦值
-
常見的使用場景
-
一些Tips
ES6允許我們通過官方規定的手法從可遍歷數據結構中直接提取值並且賦到我們創造的變量中, 這種操作被稱之爲解構賦值(Destructuring)
數組的結構賦值
以前我們從一個數組中取值, 大概是這樣
var arr = [1, 2, 3];
var first = arr[0];
var second = arr[1];
console.log(first, second); // 輸出1, 2
這種場景其實是頻繁的多, 我們總是可能要去取數組中的某一位值, 但是這樣寫的話, 官方也總覺得彆扭, 而且不夠靈活, 假如我們要取arr的前三位, 那我們勢必是需要從arr[0]寫到arr[3], 於是在ES6中, 官方推出了一種新的寫法
const arr = [1, 2, 3];
const [first, second, third] = arr;
console.log(first, second, third); // 輸出1, 2, 3
const fixArr = [[1, 2, 3], [4, 5, 6]];
const [firstArr, secondArr] = fixArr; // firstArr 爲[1, 2, 3], secondArr 爲[4, 5, 6]
const [[fstArrson], [secArrson]] = fixArr; // fstArrson 爲1, secArrson爲4
上面的方法的本質其實也是一種結構的匹配, 只要等號左邊和等號右邊的數組結構相同或者等號右邊爲可遍歷的數據結構, 左邊的變量就會被賦予相同的值, 同時如果是按順序取值, 那麼只要對應順位匹配的人結構相同即可, 我們再來看一些規則
- 如果左右兩邊的總數據結構統一, 而子元素個數不統一, 則會按照從左到右的格式進行不完全解構
const arr = [1, 2, 3];
const [first, second] = arr; // 此時first和second被賦值爲1, 2
const arr2 = [1, 2, 3];
const [first, third] = arr; // 即使我想要獲取數組的順位第三, 這樣寫也只能獲得1, 2
- 如果不想按順序獲取所有內容, 則用逗號隔開
// 比如我就想獲取數組的第一位和第三位
const arr = [1, 2, 3];
const [first, , third] = arr; // 此時first和third的值分別爲1和3, 因爲我們用逗號將中間隔出一個順位, 代表第二位不需要結構
- 如果解構數組不成功, 則會定義默認值undefined
const arr = [1, 2]
const [first, second, third] = [1, 2];
// 很明顯被解構數組只有兩位,我們的third勢必是取不到值的, 所以first -> 1, second -> 2, third -> undefined, 而我們用的const的話等於將third 賦值爲undefined且無法改變
rest運算符
rest運算符寫法就是...
關於這個rest運算符筆者在後面會詳細進行介紹, 這裏先混個臉熟, 這哥們就是把零散的值裝到一起, 或者把裝在一起的值散列開, 具體怎麼運作還是看場景,有了這個rest運算符, 那我們解構又可以這麼玩了
先讀一句話,在解構中使用rest運算符會導致在解構過程中, 帶有rest運算符的變量會直接匹配前面匹配完成的剩餘所有值並將他們裝進一個數組, 同時也代表這個帶有rest運算符的變量是收集剩餘的所有變量進一個數組, 所以他必須是最後一位, 否則會報錯
const arr = [1, 2, 3];
const [first, ...restArr] = arr;
// 上面的代碼 等於我定義了兩個變量 一個first變量, 另一個restArr變量,
// 由於restArr前面有三個點, 所以他在解構時, 會直接匹配前面匹配完成的剩餘所有值並將他們裝進一個數組賦給restArr
// 所以上方的代碼結果爲: first -> 1, restArr -> [2, 3]
const arr = [1, 2, 3];
const [first, second, ...restArr] = arr;
// first -> 1, second -> 2, restArr -> [3]
const arr = [[1, 2, 3], [4, 5, 6]];
const [first, ...restArr] = arr;
// first -> [1, 2, 3], restArr -> [[4, 5, 6]]
const arr = [1, 2, 3, 4];
// 下面這樣寫會直接報錯: SyntaxError: Rest element maust be last element
const [first, ...restArr, fourth] = arr;
同時如果帶有rest運算符的變量結構不成功, 默認值不是undefined, 而是[]
(空數組)
const arr = [1, 2];
const [first, second, ...restArr] = arr;
// first -> 1, second -> 2, restArr -> []
不可解構的值
如果等號右邊的值是不可遍歷的結構, 則會報錯, 這裏你不必瞭解太多因爲涉及到後方的ES6的其他知識, 當你對ES6掌握更加深入對的時候再回來看會有一些新的感悟
const [first] = 1; // 報錯: TypeError: 1 is not iterable,
// undefined, NaN, null, {}, false都會報錯
const [fstStr] = 'abc'; // fstStr -> 'a'
變量默認值
ES6允許如果結構賦值中變量未從右邊的數據結構中解構到對應的值, 但是我們又不想讓他默認值爲undefined
或者[]
的話, 可以對其進行默認值處理
如果變量可以解構到值, 則不會走默認值
const arr = [3, 4]
const [first = 1, second = 2, third = 3] = arr;
// 此時first -> 3, second -> 4, third走了默認值3
注意ES6內部是在使用恆等於===
來判斷右側數據結構中對應值是否嚴格等於undefined, 只要嚴格等於undefined就會走默認值
const arr = [1, undefined];
const [first = 2, second = 3] = arr;
// first -> 1, second -> 3 因爲arr的第二位已經恆等於undefined, 所以second會走默認值
const arr2 = [1, null];
const [fst = 2, sec = 3] = arr2;
// fst -> 2, sec -> null
默認值可以引用解構賦值的其他變量的值, 前提是被引用變量必須必須已經得到聲明
const [fst = 1, sec = fst] = []; // fst -> 1, sec - >
const [fst2 = 1, sec2 = fst2] = [10]; // fst2 -> 10, sec2 -> 10
const [fst3 = 1, sec3 = fst3] = [4, 5]; // fst3 -> 4, sec3 -> 5
const [fst4 = sec4, sec4 = 1] = [2, 3]; // 報錯因爲fst4使用sec4的時候sec4還未得到聲明
對象的解構賦值
如果數組的解構讓你覺得需求不是那麼的大的, 對於我個人而言, 對象的解構賦值讓我用了以後就是停不下來
在ES5中我們要獲取一個對象中的屬性值, 基本是按照如下操作的
var obj = {
name: 'alice',
age: 18,
sex: 'female'
}
var name = obj.name;
var age = obj.age;
console.log(name, age); // 輸出alice, 18
讓我們看看用ES6中的解構賦值將如何書寫
const obj = {
name: 'alice',
age: 18,
sex: 'female'
}
const {name, age} = obj;
console.log(name, obj); // 輸出alice, 18
看到上方的操作, 你想到了啥, 我當時想到的是 從今以後如果我只需要對象裏的某兩個屬性我再也不用把一個對象都拿過來操作了
順道我們來看對象的解構有哪些新的規則
- 在對象的解構賦值中, 因爲對象不是想數組一樣是有序排列的, 所以一般情況下等號左邊往往以鍵值對的形式出現, 如
const { 你要找的變量: 你希望將找到的屬性值賦值給哪個變量 } = 一個對象, 且這是固定格式不可省略
例如:
const obj = {
name: 'loki',
sex: 'male'
}
const { name: userName, sex: userSex } = obj;
// 此時userName -> loki, userSex -> male
// 上面的含義是我去obj中找到 name和sex兩個key 並將他們的屬性值value賦值給userName 和userSex
- Es6提供給我們一個語法糖, 即如果我們要找的變量和希望被賦值的變量同名的話, 我們可以寫成如下
const obj = {
name: 'loki',
sex: 'male'
}
const { name, sex } = obj;
// 此時name -> loki, sex -> male
// 上面的寫法等同於 const { name: name, sex: sex } = obj;
但是隻要你要找得key值和你希望被賦值的key值不是一致的, 該語法糖不會生效
- 我們同樣可以解構複雜的對象, 只要數據解構對的上
const obj = {
name: 'loki',
age: 18,
address: {
province: '上海'
}
}
const { name, address:{ province } } = obj;
// 上面的寫法代表我先去找obj中name屬性, 同時我外邊定義了一個name屬性接着他
// 所以name -> loki
// 然後我在找obj的address屬性, 然後我拿到address屬性我還要進行解構, 我只拿他的province屬性
// 並且我外部定義了一個province屬性來接受他, 所以province -> 上海
在對象的解構中, 你始終要清晰一點就是:
冒號前邊的是你要去找的人, 冒號後邊的是你要將這個人找到了以後怎麼樣
所以我們再來看個例子
const obj = {
name: 'loki',
age: 18
}
let newObj = {};
({name: newObj.name, age: newObj.age} = obj);
// 此時newObj 如下
// {
// name: 'loki',
// age: 18
// }
上面我們是要幹啥, 我們想找到obj中的name和age, 將他們的值賦給newObj中的name和age, 在此種情況下, 我們必須用個小括號從外部整句代碼括起來, 以告訴js引擎這是一個表達式, 否則會報錯, 關於小括號的問題, 在稍後會詳細介紹
所以對象解構更加的靈活是毋庸置疑的, 我們甚至可以將一個對象中的兩個屬性解構出來賦值給一個對象和一個數組
const obj = {
name: 'loki',
age: 18
}
let arr = [];
let newObj = {};
({ name: newObj.name, age: arr[0]} = obj);
// 此時, newObj和arr分別如下
// newObj: {
// name: 'loki'
// },
// arr: [18]
- 同樣如若解構失敗, 則變量會被賦上默認值爲undefined
const obj = {
name: 'loki'
}
const { age } = obj; // age爲undefined
對象的解構依舊可以使用rest運算符
const obj = {
name: 'loki',
age: 18,
sex: 'male'
}
const { name, ...restObj } = obj;
console.log(name, restObj);
// name -> loki restObj -> { age: 18, sex: 'male'}
對象解構也可以給予默認值, 同樣默認值生效的條件時被解構對象該屬性恆等於(===
)undefined
const { name = 'loki' } = {}; // name值爲loki
const { name: userName = 'loki' } = { age: 18 }; //userName -> loki
// 上方代碼是去等號右邊對的對象中尋找name, 並且賦值給userName
// 如果未找到, 則userName默認值爲loki
字符串的結構賦值
將對象和數組講完以後, 也算是兩個大頭搞定了, 剩下的都是一些小貓小腳, 我們來說說字符串的解構賦值
因爲字符串本身也是一個可遍歷的數據結構, 有同學說字符串是通過包裝類不是它本身可以遍歷啊, 你甭管他怎麼實現遍歷的, 你直接for循環不用其他操作可以取值就是可遍歷
字符串遍歷很簡單, 規則跟數組基本一致
const str = 'abc';
const [fst, sec, third] = str; // fst -> 'a' , sec -> 'b', third -> 'c'
const [fst2, ...restArr] = str; // fst2 -> 'a', restArr -> ['b', 'c']
const [fst3, , ,fourth = 'd'] = str; // fst3 -> 'a', fourth -> d
記住字符串在遍歷過程中會轉化爲類數組, 所以他一定有一個length屬性
const str = 'abc';
const [length] = str; // length -> 3
數值和布爾值的解構
數值和布爾值的解構的時候, 內部會隱式的進行包裝類, 由於調用包裝類形成的對象爲下圖, 其中的[[PrimitiveValue]]
我們無法訪問, 所以對數值和布爾值的解構本身是沒有意義的, 但是我們依然可以拿到一些他身上的方法, 儘管也沒任何意義, 但是至少我們可以證明他可以被解構
let {toString: s, toFixed: f, valueOf: v } = 123;
console.log(s, f, v); // 輸出原型上的三個函數
console.log(s === Number.prototype.toString); // true
函數參數的解構賦值
這個算是比較常用的了, 平時我們總是會向函數中傳遞參數, 函數參數的解構賦值讓我們的代碼變得更加簡潔也更加容易閱讀
// 參數爲一個數組的情況下, 我直接解構了這個數組, 在函數中我們就可以直接用x, y了
function add([x, y]) {
return x + y;
}
add([1, 2]);
// 在日常開發中, 我們可能會有一些渲染的操作, 數據結構傳遞過來可能是一個對象
// 但是這個對象我們確實只要用到其中的一部分屬性, 如果是ES5的情況我們可能爲了簡潔操作直接
// 將對象傳遞過來, 但是ES6 我們有了結構, 可以穩穩的拿到我們需要的屬性也不必費太多的周折
function renderStudentInfo({ name, age }) {
console.log(name, age); // 輸出loki, 18
}
const studentObj = {
name: 'loki',
age: 18,
sNo: 123,
address: {
province: '上海'
}
}
renderStudentInfo(studentObj)
解構賦值的常用場景
- 交換變量的值
這個問題我相信大家一定不會陌生, 當我們剛剛接觸編程的時候, 總是會編寫讓兩個變量的值交換的過程, 在以前我們往往需要這麼寫
var a = 10;
var b = 20;
// 交換a和b的值
a = a + b;
b = a - b;
a = a - b;
console.log(a, b); // 20 , 10
或者需要藉助第三方變量
var a = 10;
var b = 20;
var c = a;
a = b;
b = c;
console.log(a, b); //20 , 10
在解構中一切變得很簡單
let a = 10;
let b = 20;
([b, a] = [a, b])
console.log(a, b); // 20, 10
- 加載模塊
使用ES6的模塊化或者commonjs的模塊的時候, 我們知道暴露出去的往往是一個對象, 我們可以通過解構的方式來拿我們想要的數據
moduleA.js
export const name = 'loki';
export const age = 18;
moduleB.js
// B文件要使用A文件中的變量name和age, 則需要導入
import { name, age } from './moduleA.js';
console.log(name, age); // loki, 18
- 可以很方便的將現有對象的方法提取出出來
const { random, log, sin } = Math;
其實還有很多其他的應用場景比如提取json數據, 遍歷map解構等, 不想一一在寫了, 朋友在開發中慢慢用熟了就知道應用場景了
一些Tips
- 由於數組本質上是特殊的對象, 所以我們可以使用對象對數組進行解構
const arr = [1, 2, 3];
let {0: fst, 1: sec, ...restArr } = arr;
console.log(fst, sec, restArr); // 1, 2, {2: 3}
// restArr的 {2: 3}, 2 是arr數組的索引
- 解構賦值允許等號左邊的模式中不放置任何變量名, 因此可以寫出一些怪異的表達式
({} = [true, false]);
({} = []);
({} = 'abc');
上面的表達式毫無意義, 但是卻不會報錯, 所以面試要注意了哦