ES6(三: 变量的解构赋值)
来源:互联网 发布:linux 文件不保存退出 编辑:程序博客网 时间:2024/05/18 09:20
前言
你必须很努力很努力,才能看起来毫不费力
(一)数组的解构赋值
在ES6之前对变量的赋值为:
var a = 1, b = 2, c = 3;
ES6 允许我们这样写
const [a, b, c] = [1, 2, 3];// babel编译就是上面的方式
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
本质上,这种写法属于“模式匹配”,只要等号两边的模式相同,左边的变量就会被赋予对应的值。
下面是一些使用嵌套数组进行解构的例子。
解构赋值不仅适用于var命令,也适用于let和const命令。
(1)模式匹配解构
let [foo, [[bar], baz]] = [1, [[2], 3]];console.log(foo); // 1console.log(bar); // 2console.log(baz); // 3
(2)展开…
let [head, ...tail] = [1, 2, 3, 4];console.log(head); // 1console.log(tail); // [2, 3, 4]
(3)如果解构不成功,变量的值就等于undefined 。
const [foo1] = [];const [bar, foo2] = [1];console.log(foo1); // undefinedconsole.log(bar); // 1console.log(foo2); // undefined
看下babel编译成ES5
var _ref = [], foo1 = _ref[0];var _ref2 = [1], bar = _ref2[0], foo2 = _ref2[1];console.log(foo1); // undefinedconsole.log(bar); // 1console.log(foo2); // undefined
上面代码foo1和foo2都不会解构成功,他们都是undefined。
(4)另一种情况是不完全解构
即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以
成功。
let [x, y] = [1, 2, 3];x // 1y // 2
(5)对于Set结构,也可以使用数组的解构赋值。
let [x, y, z] = new Set(["a", "b", "c"])x // "a"
事实上,只要某种数据结构具有Iterator接口,都可以采用数组形式的解构赋值。
(6)解构赋值允许指定默认值。
const [foo3 = '默认值',foo4 = '不生效默认值'] = [,null];console.log(foo3); // '默认值'console.log(foo4); // null
上面代码foo3对应undefined 所以默认值生效,而foo4对应null,默认值不生效。
原因是: ES6内部使用严格相等运算符(=== ),判断一个位置是否有值。所以,如果一个数组成员不严格等于undefined ,默认值是不会生效的。
(二)对象的解构赋值
(1)解构不仅可以用于数组,还可以用于对象。
const { bar, foo, test2, test } = { foo: "aaa", test: 'haha',bar: "bbb" };console.log(bar);// "bbb"console.log(foo);// "aaa"console.log(test2);// undefinedconsole.log(test);// "haha"
上面代码体现对象的解构与数组有几个重要的不同。
(1)数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属
性没有次序。
(2)变量必须与属性同名,才能取到正确的值。
我们来看下babel编译过后就知道为什么了。
var _foo$test$bar = { foo: "aaa", test: 'haha', bar: "bbb" }, bar = _foo$test$bar.bar, foo = _foo$test$bar.foo, test2 = _foo$test$bar.test2, test = _foo$test$bar.test;console.log(bar); // "bbb"console.log(foo); // "aaa"console.log(test2); // undefinedconsole.log(test); // "haha"
(2)ES6提供当变量名与属性名不一样的写法
const obj = { first: 'hello', last: 'world' };const { first: f, last: l } = obj;console.log(first); // ReferenceError: first is not definedconsole.log(f); // hello
上面代码说明,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,
而不是前者。
让我们来看下babel编译
var obj = { first: 'hello', last: 'world' };var f = obj.first, l = obj.last;console.log(first); // ReferenceError: first is not definedconsole.log(f); // hello
这里就有个注意点。
采用这种写法时,变量的声明和赋值是一体的。
对于let和const来说,变量不能重新声明,所以一旦赋值的变量以前声明过,就会报错。(// SyntaxError: Duplicate declaration “f”)
(3)数组一样,解构也可以用于嵌套结构的对象并且可以赋默认值
const node = { loc: { start: { line: 1, column: 5 } }};const { loc: { start: { line, column: c,test = '默认值' }} } = node;console.log(line); // 1console.log(c); // 5console.log(test); // '默认值'console.log(loc); // error: loc is undefinedconsole.log(start); // error: start is undefined
上面代码loc和start都是模式,本身不会被赋值。而c是column的真正赋值,test本身是一个赋默认值的操作。
当然,生效的条件和数组一样,都是对象的属性值严格等于undefined 。
(4)解构失败,变量的值等于undefined
var {foo} = {bar: 'baz'}foo // undefined
(5)如果解构模式是嵌套的对象,而且子对象所在的父属性不存在,那么将会报错。
// 报错var {foo: {bar}} = {baz: 'baz'}
等号左边对象的foo 属性,对应一个子对象。该子对象的bar 属性,解构时会报错。原因很简
单,因为foo 这时等于undefined ,再取子属性就会报错。
看下babel编译就知道为什么了。
var _baz = { baz: 'baz' }, bar = _baz.foo.bar;
(6)已声明的变量用于解构赋值
let x;{x} = {x: 1}; //SyntaxError: Unexpected token =
上面代码的写法会报错,因为JavaScript引擎会将{x} 理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免JavaScript将其解释为代码块,才能解决这个问题。
let x;({x} = {x: 1});console.log(x); // 1
上面代码将整个解构赋值语句,放在一个圆括号里面,就可以正确执行。
关于圆括号与解构赋值的关系,参见下文。
(7)已声明的变量用于解构赋值
对象的解构赋值,可以很方便地将现有对象的方法,赋值到某个变量。
let { log, sin, cos } = Math;
上面代码将Math 对象的对数、正弦、余弦三个方法,赋值到对应的变量上,使用起来就会方便很多。
(三)字符串的解构赋值
字符串也可以解构赋值。
这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';console.log(a); // hconsole.log(b); // econsole.log(c); // l
类似数组的对象都有一个length 属性,因此还可以对这个属性解构赋值。
const { length: len} = 'hello';console.log(len); // 5
(四)数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。
const {toString: s1} = 123;console.log(s1 === Number.prototype.toString); // trueconst {toString: s2} = true;console.log(s2 === Boolean.prototype.toString); // true
上面代码中,数值和布尔值的包装对象都有toString 属性,因此变量s1,s2 都能取到值。
解构赋值的规则是,只要等号右边的值不是对象,就先将其转为对象。由于undefined 和null 无法转为对象,所以对它们进行解构赋值,都会报错。
const { prop: x } = undefined; // TypeErrorconst { prop: y } = null; // TypeError
(五)函数参数的解构赋值
(1)对函数的参数使用解构赋值。
function add([x, y]){ return x + y;}console.log(add([1, 2])); // 3
上面代码中,函数add 的参数实际上不是一个数组,而是通过解构得到的变量x 和y 。
下面是另一个例子。
[[1, 2], [3, 4]].map(([a, b]) => a + b)// [ 3, 7 ]
(2)对函数参数的解构使用默认值。
function move({x = 0, y = 0} = {}) { return [x, y];}move({x: 3, y: 8}); // [3, 8]move({x: 3}); // [3, 0]move({}); // [0, 0]move(); // [0, 0]
上面代码中,函数move 的参数是一个对象,通过对这个对象进行解构,得到变量x 和y 的值。如果解构失
败, x 和y 等于默认值。
下面是对整个参数设置默认值。
undefined 就会触发函数参数的默认值。
function move({x, y} = { x: 0, y: 0 }) { return [x, y];}move({x: 3, y: 8}); // [3, 8]move({x: 3}); // [3, undefined]move({}); // [undefined, undefined]move(); // [0, 0]
上面代码是为函数move 的参数指定默认值,而不是为变量x 和y 指定默认值,所以会得到与前一种写法不同的结果。
(六)圆括号问题
ES6的规则是,只要有可能导致解构的歧义,就不得使用圆括号。
但是,这条规则实际上不那么容易辨别,处理起来相当麻烦。因此,建议只要有可能,就不要在模式中放置圆括号。
这个不常用,这里不讲了。需要知道自己去了解。哈哈!
(七)用途
乱七八糟讲了很多问题,那么解构赋值到底有什么用途呢。我们来看看!
首先记住,数组解析是有序的匹配,对象解析是无序的。这点很重要!
(1)交换变量的值
[y,x]= [x=1,y=2];console.log(x); //2console.log(y); //1
这是babel的转译
y = x = 1;x = y = 2;console.log(x); //2console.log(y); //1
上面代码交换变量x和y的值,这样的写法不仅简洁,而且易读,语义非常清晰。
(2)从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就非常方便。
// 返回一个数组const Array = () => [1, 2, 3];const [a, b, c] = Array();console.log(a); // 1console.log(b); // 2console.log(c); // 3// 返回一个对象const Obj =() => { return {foo: 11, bar: 12}};var { bar, foo} = Obj();console.log(foo); // 11console.log(bar); // 12
上面代码分别返回数组和对象,然后通过解析非常方便得到对应的值了。
(3)函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值const f1 = ([x, y, z]) => { console.log(x+';'+y+';'+z);};f1([1, 2, 3]); // 1;2;3// 参数是一组无次序的值const f2 = ({x, y, z}) => { console.log(x+';'+y+';'+z);};f2({z: 3, y: 2, x: 1}); // 1;2;3
上面代码分别通过有序数组和无序对象定义参数的方式,将变量对应起来。
是不是感觉很常用?对就是react里面
1.首先在父组件中
render= () =>{ const collapsed = this.props.app.collapsed, handleToggle = this.handleToggle.bind(this); // header 属性集合 const headerProps = { collapsed, handleToggle }; return ( <div> <HeaderComponent {...headerProps}/> // 这是展开 </div> )}
上面代码父组件通过对象展开,直接省去在组件上定义一大堆props的不简洁写法。
2.然后我们看子组件怎么解析
const HeaderComponent = ({collapsed, handleToggle}) => ( <Header> <h4 onClick={ () =>(handleToggle()) }> <Icon className="trigger" type={collapsed ? 'menu-unfold' : 'menu-fold'} /> </h4> </Header> );
上面代码子组件是一个stateless组件,通过对象解析赋值,直接省去写 props.xx 的不简洁写法
(4)提取JSON数据
解构赋值对提取JSON对象中的数据,尤其有用。
const jsonData = { id: 42, status: "OK", data: [867, 5309]};const { id, status, data: number } = jsonData;console.log(id, status, number);// 42, OK, [867, 5309]
上面代码可以快速提取JSON数据的值。
(5)函数参数的默认值
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’; 这样的开关赋予默认值的语句。
(6)遍历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 worl
如果只想获取键名,或者只想获取键值,可以写成下面这样
// 获取键名for (let [key] of map) {// ...}// 获取键值for (let [,value] of map) {// ...}
(7)输入模块的指定方法
const { SourceMapConsumer, SourceNode } = require("source-map");import React, { PropTypes } from 'react';
- ES6(三: 变量的解构赋值)
- 【ES6】变量的解构赋值
- ES6 变量的解构赋值
- es6 变量的解构赋值
- ES6:变量的解构赋值
- ES6变量的解构赋值
- es6 变量的解构赋值
- ES6变量的解构赋值
- ES6--变量的解构赋值
- ES6--变量的解构赋值
- es6-变量解构赋值
- ES6-变量解构赋值
- ES6变量解构赋值
- ES6变量的解构赋值--读书笔记
- 自学-ES6篇-变量的解构赋值
- ES6--变量的声明及解构赋值
- ES6(四) 变量的解构赋值
- ES6学习笔记:变量的解构赋值
- [BZOJ2097][Usaco2010 Dec]Exercise 奶牛健美操(二分+树形dp+贪心)
- C++项目总结一之内存泄漏检测
- 关于redis的搭建
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 L. The Heaviest Non-decreasing Subsequence Problem (LIS)
- Android编译Lame的全平台so库方案2,并实现转码mp3
- ES6(三: 变量的解构赋值)
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 L题 非递减权值最大
- 2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 J. Minimum Distance in a Star Graph
- Python爬虫入门-fiddler抓取手机新闻评论
- MAC下升级MySQL数据库
- 关于python文件 I/O 操作 与异常
- 卷积神经网络详解
- 动态规划——开餐馆
- 大数据的大价值:大数据五大成功案例深度解析