ES6新增的部分语法
1.let和const
let和const的出现是为了弥补var,其中let是声明变量,const是声明常量,这两个变量主要解决了以下两个问题
(1)变量提升
var变量会发生‘变量提升’现象,即变量在声明之前使用,值为undefined
console.log('testVar:', testVar) // testVar: undefined
var testVar = 17
console.log('testLet', testLet) // 报错ReferencEerror
let testLet = 16
当代码复杂度越来越大时,变量提升问题容易造成混乱,使得代码可读性差,不利于迭代和维护
(2)新增块级作用域
所谓块级作用域,即规定一个变量(常量)仅在所声明的区域里面有效,一般这区域由{}隔开。习惯c或java的朋友(包括我自己)会习惯性默认这个概念,但在JS的var声明的变量中并非如此
function f() {
var n = 7
if (true) {
var n = 10
}
console.log('n:', n) // n: 10
}
f()
以上代码中,可看到n最终输出10,说明if花括号里面的变量n只是重新赋值而不是重新声明,用var声明的变量不存在块级作用域 ,我们把var转换成let
function f() {
let n = 7
if (true) {
let n = 10
}
console.log('n:', n) // n: 7
}
f()
会发现在if大括号里面声明的变量n不会作用到外面的区域,即存在块级作用域。另外在一个块级作用域中,不允许重复声明一个变量
2.解构赋值
解构赋值是一种更简便的赋值方式。按照一定的模式在数组或对象中提取值(提取过程即解构),然后对变量进行赋值。
(1)数组的解构赋值
在以前的代码中,为变量赋值只能一一指定值,如
let x = 1
let y = 2
let z = 3
在ES6中允许这样使用
let [x, y, z] = [1, 2, 3]
console.log(x, y, z) //1 2 3
x,y,z正常拿到对应的值
以下还有几种常见情况
// 1.用,隔开能准确提取相应的值
let [, , JS] = [1, 2, 'cool']
console.log('JS:', JS) // JS: cool
// 2.存在解构不成功的情况,赋值默认为undefined
let [x, y, z] = [1, 2]
console.log('x:', x) // x: 1
console.log('y:', y) // y: 2
console.log('z:', z) // z: undefined 等式右边的模式少一个元素(也可看作等式左边的模式多了一个元素),此时z的值为undefined
// 3.默认赋值
let [x = 1] = [1]
console.log(x) // 1
let [x = 1, y = x] = []
console.log(x, y) // 1 1
let [x = 1, y = x] = [1]
console.log(x, y) // 1 1
let [x = 1, y = x] = [2]
console.log(x, y) // 2 2
let [x = 1, y = x] = [1, 2]
console.log(x, y) // 1 2
// 注:默认值不能是未声明的值
let [x, y = z] = []
console.log(x, y) // 报错ReferenceError: z is not defined
(2)对象的解构赋值
let {y, x} = {x:'aaa', y:'bbb'}
console.log('x:', x) // x: aaa
console.log('y:', y) // y: bbb
以上代码是对象的解构赋值的基本用法,可看到赋值是无序的,机制会自动寻找同名属性。以下聊聊解构赋值的机制:
let {x:foo, y:bar} = {x:'aaa', y:'bbb'}
console.log(foo, bar) // aaa bbb
// 从上面的输出结果来看,解构赋值成功给变量foo和bar赋值,可以尝试输出x,y,系统将会报错
// 由此可看出对象解构赋值的内部机制是找到等式左右边的同名属性,然后再赋值给相应的变量
/*
所以
let {x, y} = {x:'aaa', y:'bbb'}
其实是
let {x:x, y:y} = {x:'aaa', y:'bbb'}
*/
对象解构赋值的机制是找到等式左右两边的同名属性,然后再赋值给相应的变量
下面是一些更为复杂的例子:
let obj = {};
let arr = [];
({x:obj.prop, y:arr[0]} = {x:'aaa', y:true});
console.log(obj) // { prop: 'aaa' }
console.log(arr) // [ true ]
对象的解构赋值也可以设置默认变量:
let {x = 3, y = 4} = {}
console.log(x, y) // 3 4
3.扩展运算符和rest参数
rest参数和扩展运算符都是ES6的新特性,rest参数的形式 ...(变量名),前面的...(3个点)即是扩展运算符
(1)rest参数:用于获取函数多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入其中。
function add(...args) {
console.log(args) // [1,2,3]
let sum = 0
for (let i of args) {
sum += i
}
return sum
}
console.log(add(1,2,3)) // 6
以上代码中,给add函数传递3个参数,rest参数将参数列表转化成数组存储到args中,最终成功求和
function add(x, y, ...args) {
console.log(args) // [3,4,5]
let sum = 0
for (let i of args) {
sum += i
}
return sum
}
console.log(add(1,2,3,4,5)) // 12
以上代码中,args仅获取后3个参数,x、y分别获取参数1、2
(2)数组的扩展运算符:如同rest的逆运算,将一个数组转为用逗号分隔开的参数序列
console.log(...[1,2,3]) // 1 2 3
以上代码中将数组转化成参数数列
function add(x, y) {
console.log(x, y) // 1 2
return x+y
}
const num = [1,2]
console.log(add(...num)) // 3
以上代码中扩展运算符将num数组转化成参数序列传值给add函数
以下为数组扩展运算符的一些应用
// 1.合并数组
let arr1 = [1, 2, 3]
let arr2 = [4, 5, 6]
arr1 = [...arr1, ...arr2]
console.log(arr1) // [ 1, 2, 3, 4, 5, 6 ]
// 2.用于解构赋值
let [x, y, ...arg] = [1,2,3,4,5,6]
console.log(x) // 1
console.log(y) // 2
console.log(arg) // [ 3, 4, 5, 6 ]
// 3.函数传参如上例
(2)对象的扩展运算符:形式...(对象名)
// 1.用于结构赋值 注:解构赋值必须是最后一个参数,否则会报错
let {x, y, ...z} = {
x: 1,
y: 2,
a: 3,
b: 4
}
console.log(z) // { a: 3, b: 4 }
// 2.用于参数对象的所有可遍历属性,并将其复制到当前对象中,类似于合并对象
let x = {a:1, b:2}
let y = {c:3, d:4}
let z = {...x, ...y}
console.log(z) // { a: 1, b: 2, c: 3, d: 4 }
4.数据结构Set和Map
Set,Map都是ES6新增的数据结构
Set:它类似于数组,但是成员的值都是唯一的
(1)Set可接受一个数组(或实现iterator接口的数据结构)作为参数,用于初始化
let set = new Set([1,2,3,3])
console.log(set) // Set { 1, 2, 3 }
(2)Set实例的属性和方法
- size属性:返回成员的个数
- add(value):添加一个成员,返回set本身
- delete(value):删除一个成员,返回布尔值
- has(value):判断是否存在某个成员,返回布尔值
- clear():清除所有成员,没有布尔值
let set = new Set([1,2,3])
set.add(4).add(7).add(5)
console.log(set) // Set { 1, 2, 3, 4, 7, 5 }
console.log(set.has(2)) // true
console.log(set.delete(2)) // true
console.log(set.has(2)) // false
set.clear()
console.log(set) // Set {}
Map:它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键(在普通的object中,只用用字符串作为键)
(1)Map可接受一个数组作为参数,该数组的成员是一个个表示键值对的数组
let map = new Map([
['name', 'jack'],
['old', '17']
])
console.log(map) // Map { 'name' => 'jack', 'old' => '17' }
(2)Map实例的属性和方法
- size属性:返回成员的个数
- set(key, value):设置key所对应的整个键值,然后返回整个Map结构,若key已经有值,则键值会被更新
- get(key):获取key对应的键值,若找不到key,返回undefined
- has(key):判断某个键是否存在,返回布尔值
- delete(key):删除某个键,返回布尔值
- clear():清除所有成员,没有返回值
let map = new Map([
['name', 'jack'],
['old', '17']
])
console.log(map) // Map { 'name' => 'jack', 'old' => '17' }
console.log(map.size) // 2
console.log(map.set('old', 18)) // Map { 'name' => 'jack', 'old' => 18 }
console.log(map.get('name')) // jack
console.log(map.has('name')) // true
console.log(map.delete('old')) // true
console.log(map) // Map { 'name' => 'jack' }
map.clear()
console.log(map) // Map {}
5.for...of 循环
ES6新增循环for...of,作为遍历所有数据结构的统一方法,一个数据结构只要实现了iterator接口,就可以用for...of循环遍历它的成员
(1)对数组的遍历
for (let i of [1,2,4,5]) {
console.log(i) // 1 2 4 5
}
(2)对Set和Map的遍历
let set = new Set([1,2,3,4])
for (let i of [1,2,4,5]) {
console.log(i) // 1 2 4 5
}
let map = new Map([
['name', 'jack'],
['old', '17']
])
for (let [key, value] of map) {
console.log(key, value)
}
(3)对对象的遍历
对于普通的对象,for...of循环不能直接使用,因为对象没有部署iterator接口
let obj = {
'name': 'jack',
'old': '17'
}
for (let i of obj) { // 报错 TypeError: obj is not iterable
console.log(i)
}
可以使用对象的keys(),values(),entries()方法用于遍历
let obj = {
'name': 'jack',
'old': '17'
}
for (let i of Object.keys(obj)) {
console.log(i) // name old
}
for (let [key, value] of Object.entries(obj)) {
console.log(key, ': ', value)
}
/*
name : jack
old : 17
*/
(4)for...of与forEach、for...in循环的比较
forEach:在forEach循环中,break命令或return命令都无效,循环难以中途退出
for...in:此循环专门为遍历对象而设计,每次遍历获取当前对象的key值,通过key可以拿到value,若用于遍历数组,会以字符串作为键名(将数组作为对象)输出,‘0’、‘1’、‘2’...
// for...in 遍历对象
let data = {
'123': 'abc',
'456': 'def'
}
for (let item in data) {
console.log(item, data[item])
}
/**
* 123 abc
* 456 def
*
*/
// for...in 遍历数组
let arr = ['abc', 'def']
for (let i in arr) {
console.log(i , typeof(i))
}
/*
0 string
1 string
*/
for...of语法与for...in一样简洁,可以在循环体内使用break、continue以达到中途退出,提供了遍历所有数据结构的统一操作接口
for...in循环适用于遍历对象,for...of用于遍历实现了iterator接口的数据类型