本文转载于:猿2048网站➜https://www.mk2048.com/blog/blog.php?id=h22iibhjab
写在前面
我们首先会讨论如何在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) // -------- 1 2 3 4 var [a, , , d, [x, y, z]] = nestedArray; console.log(a, d, x, y, z) // -------- 1 4 7 8 9
使用rest参数语法,可以省略参数的个数:
var [a, b, c] = array; console.log(a, b, c) // -------- 1 2 3 // 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 // 一个更为抽象的例子(稍微有点偏题,😁) 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方法 - 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!
参考资料
- Gist - Several demos and usages for ES6 destructuring
- ES6 JavaScript Destructuring in Depth
- Why Destructuring is a Terrible Idea in ES6
- redux
- ES6 quiz
本文首发于http://kissyu.org/2018/09/11/【译】快速入门ES6解构以及rest参数语法/ 欢迎评论和转载! 订阅下方微信公众号,获取第一手资讯!
