【譯】快速入門ES6解構以及rest引數語法
我們首先會討論如何在ES6中對陣列以及物件使用解構(destructing)和rest引數語法。然後我們接下來再看一些例子,並且討論一些quiz。
陣列
var array = [1, 2, 3, 4]; var nestedArray = [1, 2, 3, 4, [7, 8, 9]]; var [a, b, c, d] = array; console.log(a, b, c, d) // -------- 1234 var [a, , , d, [x, y, z]] = nestedArray; console.log(a, d, x, y, z) // -------- 14789
使用rest引數語法,可以省略引數的個數:
var [a, b, c] = array; console.log(a, b, c) // -------- 123 // rest parameter var [a, b, ...c] = array; console.log(c); // [3, 4]
然而當使用rest引數的時候,你必須將它放置在最後一個位置:
var [...head, d] = array; // Uncaught SyntaxError: Unexpected token...
當解構的引數個數超過了陣列中元素個數的時候,多出來的引數的值會是undefined:
var [a, b, c, d, e] = array; console.log(e); // undefined
但是我們可以對引數設定預設值,這樣就不用擔心出現undefined啦:
var [a, b, c, d, e = 5] = array; console.log(e); // -------- 5
我們可以輕而易舉地拷貝一個數組:
var [...clonedArray] = array; console.log(clonedArray); // [1, 2, 3, 4] // 拷貝一個巢狀陣列 var [,,,, [...clonedNestedArray]] = nestedArray; console.log(clonedNestedArray); // [7, 8, 9]
還可以輕而易舉地交換元素的值:
var a = 1, b = 2; [b, a] = [a, b];
這是早先JavaScript獲取函式入參的程式碼:
function foo() { return Array.prototype.slice.call(arguments, 0); } foo(1,2,3,4,5) // [1, 2, 3, 4, 5]
現在我們可以將它重構成更精簡的程式碼:
function foo(...args) { return args; } foo(1,2,3,4,5) // [1, 2, 3, 4, 5]
物件
var object = {a: "A", b: "B", c: "C"}; var nestedObject = {a: "A", b: "B", c: "C", x: {y: "Y", z: "Z"}}; var {a: A, b: B, c: C} = object; console.log(A, B, C); // ------ "A" "B" "C" var {a: A, b: B, c: C, x: X} = nestedObject; console.log(X); // {y: "Y", z: "Z"}
如果我們僅需要物件中的一個欄位的話:
var {b: B} = object; console.log(B); // ------- "B"
與陣列類似,我們同樣可以對引數設定預設值:
var {b: B, d: D = "D"} = object; console.log(B, D); // ------- "B" "D" // 如果物件中沒有對應的key的話,將會返回undefined var {a: A, b: B, d: D} = object; console.log(A, B, D); // ------- "A" "B" undefined
如果我們每次都要顯式地書寫{ keys: newVariable }
,那未免過於囉嗦了。所以我們可以採用簡寫的形式:
var {a, b, c} = object; console.log(a, b, c); // -------"A" "B" "C"
乍一看可能會有些困惑,其實上面這段程式碼本質上等同於:
var {a: a, b: b, c: c} = object; // 新的a, b, c 將會被建立
顯然,如果你想取不同的引數名,那麼就無法使用簡寫形式。
讓我們繼續看一下其他例子:
var object = {a: "A", b: "B", c: "C"}; var nestedObject = {a: "A", b: "B", c: "C", x: {y: "Y", z: "Z"}}; var {a, b, c, x} = nestedObject; console.log(x); // { y: "Y", z: "Z" } var {b} = object; console.log(b); // ------- "B" var {b, d = "D"} = object; console.log(d, d); // ------- "B" "D" // 如果物件中沒有對應的key的話,將會返回undefined var {a, b, d} = object; console.log(a, b, d); // ------- "A" "B" undefined
在解構物件的時候,同時使用rest引數語法可能會導致失敗:
// error // var {a: A, b: B, c: ...C} = object; // console.log(A, B, C);<-- error // use the shorthand method var {a, b, ...c} = object;// es7 console.log(c); // {c: "C"}
接下來展示一下當使用物件解構語法,我們對程式碼可以做到怎樣的精簡:
var John = {name: "John", age: 20}; var Marry = {name: "Marry"}; function getAge(person) { age = person.age || 18 // 預設值 return age } getAge(John);// 20 getAge(Marry); // 18 // with es6 function getAge({age = 18}) { return age }
舉例
讓我們看一下在實際編碼中,解構以及rest引數語法的使用場景。首先我們看一下函式的可變引數實現:
function sum(...numbers) { return numbers.reduce((n, total) => { return n + total }, 0); } sum();// 0 sum(1, 2, 3);// 6 sum(1, 2, 3, 4, 5);// 15 // 一個更為抽象的例子(稍微有點偏題,:grin:) function math(equation, ...numbers) { return numbers.reduce((n, total) => { return equation.call(null, n, total); }); } const add = (a, b) => { return a + b; } let sum1 = math(add, 1, 2, 3, 4); // 10 const times = (a, b) => { return a * b; } let product1 = math(times, 1, 2, 3) // 6
Redux
讓我們在redux中尋找一些例子。在redux原始碼中,存在一個util方法 -ofollow,noindex">compose ,它常被用來簡化程式碼:
function (arg) { return fn1(fn2(fn3(arg))); }
使用compose:
function(arg) { compose(fn1, fn2, fn3)(arg) }
讓我們看一下compose實際做了啥,這是一個精簡的原始碼解釋:
export default function compose(...funcs) {// 接受一組函式作為入參,比如 fn1, [fn2, fn3...] return (...args) => { // 返回一個函式,該函式的入參比如 arg1, [arg2, arg3... ] // 由於使用了rest引數語法,此時args為一個數組: args = [arg1, arg2, ...] const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return rest.reduceRight((composed, f) => f(composed), last(...args)) // (composed, f) => f(composed)沒什麼好說的,相當於實現函式的鏈式呼叫 // 我們主要解釋last(...args)這部分 // 因為我們的函式接受的入參是last(arg1, arg2...),而不是last([arg1, arg2...]) // 所以我們需要確保[arg1, arg2, ...] 轉變成 arg1, arg2, ... // 那麼我們可以使用rest引數語法 } }
讓我們繼續看一個例子,來理解上述的args到底是如何被轉變的:
function inspect(...args) { // 當此函式被呼叫時 // args = ["a", "b", "c"] console.log(args)// ["a", "b", "c"] console.log(...args) // "a" "b" "c" } inspect("a", "b", "c")
Quiz
在ES6 JavaScript quiz 中有一些有趣的關於解構和rest引數的quiz,讓我們來看看它們吧~
Question 3
let x, { x: y = 1 } = { x }; y;
讓我們來逐步解析:
let x, // x = undefined { x: y = 1 } = { x } // 在這裡y被設定了一個預設值1 // 又由於右邊的x此時是undefined狀態 // 所以上述程式碼等同於: // { x: y = 1 } = { } y; // 所以y返回1
Question 7
[...[...'...']].length
一開始我被這段程式碼給嚇到了。但是'...'
本質上只是一個字串。讓我們簡化一下程式碼:
[...[...'str']]
由於字串是可遍歷的,所以...'str'
將返回["s", "t", "r"]
。然後又因為[...["s", "t", "r"]]
將返回["s", "t", "r"]
。所以答案很明顯,那就是3啦~
Question 11
((...x, xs)=>x)(1,2,3)
在陣列那節中已經提到過了,rest引數只能放在最後一個位置,所以上述程式碼會丟擲一個錯誤。
總結
這就是全部我要分享的內容啦~希望你能通過本文快速地掌握解構以及rest引數語法的基礎使用方式,Have fun!