ES6系列-1-命名与解构赋值
本篇主要介绍ES6新增的命名方式:let/const ,与解构赋值。
第一章 let和const
let
ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。也就是其声明的变量就绑定在这个区域中,不会再受到外界影响。
{ let a = 10; var b = 1; } console.log(b);//1 console.log(a);//报错: a is not defined.
例子2:
for (let i = 0; i < 3; i++) { let i = 100; console.log(i); } //输出 三次100,说明循环体内部是一个单独的作用域,如果换成var,则只会输出一次100。
特别对于for循环绑定事件获取索引问题可以很好的解决点我了解更多
ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
let声明,一个代码块内不可重复声明,否则会报错,而var命名则不会。
function func(arg) { let arg; // 报错 } function func(arg) { { let arg; // 不报错 } }
const
const声明一个只读的常量。一旦声明,常量的值就不能改变,
const一旦声明变量,就必须立即初始化,不能留到以后赋值。
const如果赋值一个对象时,保存的只是一个指向实际数据的指针
const obj={}; obj.name="123";//可以修改指针指向对象的数据 obj=null;//报错!,不可修改指针的指向
顶层对象属性
ES5中顶层对象的属性与全局变量挂钩,这也被认为是js语言设计最大的败笔之一,各种命名冲突,很多只有在运行过程中才被暴露出来。
let a=1;
var b=2;
const c=3;
console.log(window.a);//undefined
console.log(window.b);//2
console.log(window.c);//undefined
目前在ES6中顶层对象依旧没有统一。
- 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。
- 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。
- Node 里面,顶层对象是global,但其他环境都不支持。
变量声明的六种方式
待补充~
块级作用域
- es6 支持通过
{}
(花括号)创建块级作用域 - es6 允许块级作用域的任意嵌套,外层作用域无法读取内层作用域的变量。
- es5的块级作用域一般通过自执行函数(IIFE)来创建,块级作用域的出现,IIFE不再必要了。
- ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。ES6进行了修正,允许块级作用域内声明函数,但该函数只能在当前作用域内生效。外部无法访问(具体不同的浏览器实现会不一样)
第二章 变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
数组的解构赋值
将等号右边的数组,按照一定的匹配模式赋值给左边的变量。
基本用法
我们通常给变量赋值,采用下面写法:
let a = 1;
let b = 2;
let c = 3;
ES6 允许这样赋值
let [a, b, c] = [1, 2, 3];
上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。
注意
- 如果解构不成功,变量的值就等于undefined。
- 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
- 如果等号的右边不是数组,(或者严格地说,不是可遍历的结构),那么将会报错。
// 报错 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {};
指定默认值
解构赋值允许指定默认值。
let [foo = true] = [];
foo // true
let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。
对象的解构赋值
解构不仅可以用于数组,还可以用于对象。
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值(也可以将数组的索引认为是唯一的key,这样与对象的属性其实也是相通的)
let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"
let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
//可以这样理解:前面的部分只是变量声明,声明了变量foo和bar, 等号后面是对这两个变量进行赋值,foo的值为'aaa',bar的为'bbb'。
如果变量名与属性名不一致,必须写成下面这样(可以理解为变量名映射)
let { foo: baz } = { foo: "aaa", bar: "bbb" };
console.log(baz);//foo是匹配的模式,baz才是变量。真正被赋值的是变量baz
与数组一样,解构也可以用于嵌套结构的对象:
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p: [x, { y }] } = obj;
console.log(x,y);// Hello World
console.log(p);// 报错
这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以这样写:
let obj = {
p: [
'Hello',
{ y: 'World' }
]
};
let { p, p: [x, { y }] } = obj;
console.log(x,y);// Hello World
console.log(p);// ["Hello", {y: "World"}]
如果层级比较多,需要每一级都赋值的话,需要多次匹配
const node = {
loc: {
start: {
line: 1,
column: 5
}
}
};
let { loc, loc: { start }, loc: { start: { line }} } = node;
line // 1
loc // Object {start: Object}
start // Object {line: 1, column: 5}
字符串的解构赋值
字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。
const [a, b, c, d, e] = 'hello';
a // "h"
b // "e"
c // "l"
d // "l"
e // "o"
类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。
let {length : len} = 'hello';
len // 5
数值和布尔值的解构赋值
解构赋值时,如果等号右边是数值和布尔值,则会先转为对象,无法转换成对象的就报错(null 和 undefined)
let {toString: s} = 123;
s === Number.prototype.toString // true
let {toString: s} = true;
s === Boolean.prototype.toString // true
let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError
函数参数的解构赋值
函数的参数也可以使用解构赋值。
例子1:
function add([x, y]){
return x + y;
}
add([1, 2]); // 3
例子2:
[[1, 2], [3, 4]].map(([a, b]) => a + b); //[3, 7]
// 如果不好理解可以运行 [3, 7].map((item) => item); //[3, 7]
例子3:函数参数的解构也可以使用默认值。
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]
圆括号问题
结构语句中慎用圆括号,以下几种情况不要用圆括号,会报错:
- 变量声明语句
- 函数参数
- 赋值语句的模式
可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号
解构赋值的用途
交换变量的值
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x,y);//2,1
从函数返回多个值
函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就变得非常方便:
// 返回一个数组
function example() {
return [1, 2, 3];
}
let [a, b, c] = example();
// 返回一个对象
function example() {
return {
foo: 1,
bar: 2
};
}
let { foo, bar } = example();
函数参数的定义
// 参数是一组有次序的值
function f([x, y, z]) { console.log(x+y+z) }
f([1, 2, 3]); //6
// 参数是一组无次序的值
function f({x, y, z}) { console.log(2*x+y-z) }
f({z: 3, y: 2, x: 1});//1
提取 JSON 数据
解构赋值对提取 JSON 对象中的数据,尤其有用。
let jsonData = {
id: 42,
status: "OK",
data: [867, 5309]
};
let { id, status, data: number } = jsonData;
console.log(id, status, number);
函数参数的默认值
function share({
title="标题",
url=window.location.href,
content="分享描述"
}){
return JSON.stringify({
title:title,
shortLink:url,
content:content
})
}
console.log(share("自定义标题"));//{"title":"标题","shortLink":"http://zc.dev.bxd365.com/#/index","content":"分享描述"}
遍历 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) {
// ...
}
输入模块的指定方法(webpack中常见)
const { SourceMapConsumer, SourceNode } = require("source-map");