ES6赋值—解构赋值

作者:米书林
参考文章:《菜鸟教程》、《 ECMAScript 6 入门》(阮一峰)

解构的定义

    ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。

结构表达式的组成

1.解构的源:解构表达式值的来源,表达式的右边部分
2.解构的目标:定义解构表达式值的存储变量,表达式的左边部分

数组的解构

基本用法
let [a,b,c] = [1,2,3];
console.log(a);  \\ 1
console.log(b);  \\ 2
console.log(c);  \\ 3

    只要表达式左右的结构一样,左边对应的变量能在右边找到对应的值,就能将右边的值赋值给左边的变量。
    我们可以用解构赋值来简化后台给我们传递的数据,减少数据遍历的循环次数

可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3

若上述变量b不用[]括起来,则b返回的值带有中括号,如下:

let [a, [b, c]] = [1, [[2], 3]];
// a = 1
// b = [2]
// c = 3
可忽略
let [a, , b] = [1, 2, 3];
// a = 1
// b = 3

若被忽略项在末尾,直接忽略;若被忽略项不在末尾,则需要用","来填补,若省略",",则浏览器默认会在末尾补齐
例如:

let [a, , b] = [1, 2, 3, 4];
// a = 1
// b = 3

let [a, , , b] = [1, 2, 3, 4];
// a = 1
// b = 4
不完全解构
let [a = 1, b] = []; 
// a = 1
// b = undefined

若定义的变量在右侧无对应项,则会被赋值为:undefined;
若定义的变量给定默认值,且匹配结果为undefined,则解构的结果为默认值。

剩余运算符
let [a, ...b] = [1, 2, 3];
//a = 1
//b = [2, 3]

注意:含有剩余运算符项表达式要放在最后一项

结构源为字符串
let [a,b,c] = "ES6"
// a = "E"
// b = "S"
// c = "6"
解构默认值

    当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。

let [a = 3, b = a] = [undefined];     // a = 3, b = 3
let [a = 3, b = a] = [1];    // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2

分析:
    第一个表达式a匹配的结果为undefined,故返回a的默认值,a的默认值为3,b匹配的结果为undefined,故返回b的默认值,b的默认值为b=a,a此时为3,故b为3;
    第二个表达式a匹配的结果为1,故返回a的匹配值1,b匹配的结果为undefined,故返回b的默认值,b的默认值为b=a,a此时为1,故b为1;
    第三个表达式a匹配的结果为1,故返回a的匹配值1,b匹配的结果为2,故返回b的匹配值2;

解构匹配值为null
let [a=12] = [null]
// a = null
解构Set结构
let [a, c, c] = new Set(['a', 'b', 'c']);
//  a = "a"
//  b = "b"
//  c = "c"

不只是数组,只要能遍历的结构都能使用解构
不能遍历的结构不能被解构,例如:

let [a] = 1;
let [a] = false;
let [a] = NaN;
let [a] = undefined;
let [a] = null;
let [a] = {};

以上代码浏览器执行后都会报错。

对象模型的解构

基本
let {name,age} = {name:"张三",age:20}
// name = "张三"
// age = 20

注意:此处等号左侧的变量name是name:name缩写,age是age:age的缩写,ES6新特性规定对象的属性和值用同一变量可以简写为一个。因此:结构后的结果实际上是赋值给了对象属性值的变量

对象属性值的解构
let {name:name1} = {name:"李四"}
// name = undefined(浏览器报错:name is not defined)
// name1 = "李四"

上面代码可以看出解构的结果赋值给了对象属性值对应的变量

可嵌套
let {p: [x, { y }] } = {p: ['hello', {y: 'world'}] };
// x = 'hello'
// y = 'world'
可忽略
let {p: [x, {  }] } = {p: ['hello', {y: 'world'}] };
// x = 'hello'
let {p: [, { y }] } = {p: ['hello', {y: 'world'}] };
// y = 'world'
不完全解构
let {p: [{ y }, x ] } ={p: [{y: 'world'}] };
// x = undefined
// y = 'world'
剩余运算符
let {a, b, ...rest } = {a: "张三", b: "李四", c: "王五", d: "李二"};
// a =  "张三"
// b = "李四"
// rest = {c: "王五", d: "李二"}
解构默认值
let {a = 1, b = 2} = {a: 3};
// a = 3; b = 2;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5; 

默认解构值在匹配结果为undefined时触发。

已经声明的变量用于解构赋值

// 错误的写法
let x;
{x} = {x: 1};
// Uncaught SyntaxError: Unexpected token '='

以上代码浏览器会报错,原因是浏览器将大括号解析成了代码块,两个代码块之间要么为空格,要么为“;”,故此处报错。解决的办法是用()将它转换成一个表达式

let x;
({x} = {x: 1});

应用场景

1.任意个参数求和

传统方法:
    js传统方法需要借助arguments来实现,具体方法如下:

function sum(){
  var sum = 0;
  var len = arguments.length; // 将长度存储在变量中可以减少循环次数,提升性能
  for(var i = 0;i < len;i++){
    sum +=arguments[i]
  }
  return sum;
}
sum(1,2,3);  // 6
sum(1,2,"4"); // "34"

此处只是为了对比传统方法和ES6新方法求和,未作数值类型转化,故第二个调用出现了字符串拼接

ES6新方法:
    ES6用剩余项表达式和解构来存储参数个数,求和的方法如下:

function sum(...nums){
  let sum = nums.reduce((x,y)=>{return x+y})
  return sum
}
sum(1,2,3);  // 6
sum(1,2,"4"); // "34"

注意此处使用了ES6数组的新方法:reduce,reduce的作用是汇总,输入一堆,输出一个结果
    在这里我们可能感觉两种方法的实现原理好像是一样的额,其实并不然,传统方法借助的arguments是一个类数组,虽然我们可以像数组一样用下标,即arguments[0],arguments[1],...的方式去访问它,也能通过arguments.length来计算传入参数的个数,还能使用arguments.callee(注意在ES5中这个属性已被废弃,caller不是的属性),然而它还是没有数组常用的一些方法,比如reduce,pop,push,shift,unshift等,所以用传统方法求参数和要比ES6的代码多好几行。

2.不借助第三个参数交换变量的值

传统方法:
    传统方法(ES6之前,C语言等)要交换两个变量的值需要借助第三个变量,在js中的实现方法大概如下:

let a = 1;
let b = 2;
let c = a;
console.log(a,b,c);
a = b;
b = c;
// a = 2;
// b = 1;
// c = 1;

所以,传统方法大概需要5行代码才能实现。

ES6方法:
ES6中我们不需要借助第三个参数,具体实现方法如下:

let a = 1;
let b = 2;
[a, b] = [b, a];
// a = 2;
// b = 1;

从上面可以看出我们只需要三行代码就能完成参数的交换。

3.收集函数的剩余参数,用作公共函数可选项

    我们都知道js中传递的实参可以不和形参一一对应;
    形参个数大于实参个数时,多余的形参会被初始化为undefined(函数声明整体提升,随后找形参并将形参初始化为undefined,最后才是实参和形参相统一,所以没有实参对应的形参值仍然是undefined);
    当形参个数小于实参个数时,多余的实参会被忽略;
    因此,一些场景下即使我们只用到一次的或者可能不会用到的参数都要在定义形参来传递,一些参数个数不确定的场景下我们只能通过多定义形参来解决问题,但是这显然有些笨拙。
    在ES6新特性中,我们可以通过“...+变量名”来解决参数传递的问题。
    下面我们可以通过函数来简单模范api的可选参数,我们把必填参数放在前面,把可选参数放在剩余项里,这样我们的代码如下:

function dateFormat(a,..b){
  ...
}

    上面代码a是必填参数,b是可选参数的集合,这样我们就能通过一个变量将不常用的参数放到一个集合中了。

4.返回多个函数结果

    ES6之前我们想从函数返回多个结果,只能将它们放在数组或对象里返回,想要获取每一个返回结果,少不了循环和遍历语句,这很让人头疼。
    有了ES6后我们就可以将函数执行的结果一个一个的提取出来。大概的代码如下:

// 返回一个数组
function test() {
  return [1, 2, 3];
}
let [a, b, c] = test();

// 返回一个对象
function test() {
  return {
    foo: 1,
    bar: 2
  };
}
let { foo, bar } = test();
5.简化底层数据的调用

    很多时候后台返回给我们的都是一些对象和数组的混合嵌套体,对于深层数据的访问我们往往需要写一长串“.”引用,用解构赋值我们就可以将这些数据单独获取出来。

6.设置函数参数的默认值
jQuery.ajax = function (url, {
  async = true,
  beforeSend = function () {},
  cache = true,
  complete = function () {},
  crossDomain = false,
  global = true,
  // ... more config
} = {}) {
  // ... do stuff
};

指定参数的默认值,就避免了在函数体内部再写var foo = config.foo || 'default foo';这样的语句。

7.遍历 Map 结构

任何部署了 Iterator 接口的对象,都可以用for...of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。

const map = new Map();
map.set('first', 'hello');
map.set('second', 'world');

for (let [key, value] of map) {
console.log(key + " is " + value);
}
// first is hello
// second is world

如果只想获取键名,或者只想获取键值,可以写成下面这样。

// 获取键名
for (let [key] of map) {
  // ...
}

// 获取键值
for (let [,value] of map) {
  // ...
}
8.输入模块的指定方法

加载模块时,往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。

const { SourceMapConsumer, SourceNode } = require("source-map");

注:此文参考了菜鸟教程阮一峰博文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352