对ES6的再次补充理解
for(){}中的let
1 |
for (let i = 0; i < 3; i ++) { |
for循环有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域
所以可以在循环体内部可以再次声明一个i,和设置循环变量的i不是同一个
1 |
for (let i = 0; i < 3; i ++) { |
这次在循环体内部输出的i其实是for()中的i,即它父作用域中的i
let的暂时死区
看一个例子
1 |
var t = '123' |
存在全局变量t,但是块级作用域内let又声明了一个局部变量t,导致后者绑定这个块级作用域,所以在let声明变量前,对t赋值会报错。
暂时死区(temporal dead zone,TDZ)的意思是:** 如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错**。
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
1 |
var t = '123' |
上述例子显示的很明显,只要该作用域中有let,那么从作用域开始的地方,TDZ就开始,一直到let声明,TDZ才结束。
暂时性死区的本质:只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。
解构赋值
ES6可以从数组和对象中提取值,按照一定的格式对变量进行赋值,称为解构赋值。
1 |
let [a,b,c] = [1,2,3] |
从数组[1,2,3]中提取值,按照对应位置,对变量赋值。
解构赋值中需要注意的点:
- 不完全解构
- 带默认值的解构赋值
#函数拓展
- 参数可以设置默认值(默认值的()也是一个作用域)
- rest参数
- 箭头函数的this
#数组扩展
- 扩展运算符
- Array.from()
- Array.of()
#对象扩展
- 对象属性和方法的简写
- Object.assign()
#Symbol
- Symbol() / Symbol.for()
- Symbol.keyFor()
Set
构造函数上prototype的属性:
1,constructor,没什么好说的,所有构造函数的prototype上的constructor都指向该构造函数本身,Set也不例外。
2,size,Set.prototype.size 返回Set的实例对象的成员总数1
2
3
4
5
6
7
8
9// Set数据结构
let set = new Set()
let arr = [2,1,1,1,1,2,3,4,5,4,5]
arr.forEach((e) => {
set.add(e) // Set和数组类似 但是所有成员都是唯一的
})
log(set) //Set { 2, 1, 3, 4, 5 }
log(set.constructor) // constructor默认指向Set构造函数
log(set.size) // 返回实例的成员总数对实例的操作方法
1, add(value) 添加某个值,返回 Set 结构本身。
2, delete(value) 删除某个值,返回一个布尔值,表示删除是否成功。
3, has(value) 返回一个布尔值,表示该值是否为Set的成员。
4, clear():清除所有成员,没有返回值。1
2
3
4
5
6let set = new Set([1,2,3,5])
set.delete(2) // 返回一个boolean值 删除传入的值
log(set) //Set { 1, 3, 4, 5 }
log(set.has(3)) //true 返回一个boolean值
set.clear() // clear() 清空实例中的所有值
log(set) // Set {}对Set实力的遍历操作
1, keys():返回键名的遍历器 由于Set实例没有键值只有键名,所以keys()和values()的行为完全一致
2, values():返回键值的遍历器
3, entries():返回键值对的遍历器
4, forEach():使用回调函数遍历每个成员 可以有第二个参数,表示绑定处理函数内部的this对象。
Set的遍历顺序就是插入顺序,所以可以在Set里保存一些回调函数,遍历的时候就会按照添加时的顺序进行遍历。1
2
3
4
5
6
7
8
9
10
11// Set的遍历操作 Set的遍历顺序就是添加顺序,所以添加一些回调函数进行遍历时,就会按照添加的顺序进行遍历
let cbList = new Set();
let cb1 = () => {console.log('cb1')};
let cb2 = () => {console.log('cb2')};
let cb3 = () => {console.log('cb3')};
cbList.add(cb1)
cbList.add(cb2)
cbList.add(cb3)
cbList.forEach((cb) => {
cb() // cb1 cb2 cb3
})另外可以通过Array.from() / …(或者扩展运算符) 将Set实例转换为数组 实现数组去重
1
2
3
4// Array.from()可以转换Set实例为数组 实现数组去重
let dupeArr = [1,1,1,2,2,3]
let dedupeArr = Array.from(new Set(dupeArr)) // let dedupeArr = [...new Set(dupeArr)];
log(dedupeArr) // [1,2,3]
WeakSet
WeakSet 和 Set有两个区别
WeakSet 的成员只能是对象,而不能是其他类型的值。
大专栏 DeeJay's Blogre>1
2
3let ws = new WeakSet();
// ws.add(1); // Invalid value used in weak set
// ws.add(Symbol()); //Invalid value used in weak setWeakSet 中的对象都是弱引用(垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中),同时WeakSet也不可遍历。
Map
针对ES5中的对象结构(都是key/value格式),而key只可以是字符串的情况,ES6提出了全新的Map数据结构。Map结构类似于对象,也是键值对的形式,但是键值的范围不仅仅限于字符串。
Object: ‘字符串 - 值’
Map: ‘值 - 值’
1 |
const o = {greet: "hello, Map's key"}; |
上述例子,介绍了将一个对象o {greet: "hello, Map's key"}
作为Map结构的key进行操作。
Map()构造函数也可以接收一个数组作为参数:
1 |
const arr = [ |
在新建Map时,指定了name和title作为key。
关于这一点,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构都可以当作Map构造函数的参数。
试一下用Set作为Map的参数
1 |
let s = new Set([ |
1 |
let m1 = new Map([ |
Map结构只有对于是同一个对象的引用,才将其视为同一个键。来看下面的例子:
1 |
let m = new Map(); |
1 |
const obj1 = {name: 'DeeJay'}; //两个对象的地址不一样 虽然值是一样的 但是在Map中被视为两个键 |
通过上面的例子可以看出:Map中的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。
对于简单类型的键值(Number,String,Boolean)来说,只要二者是严格相等的,那么Map就视为同一个键(0和-0 视为同一个键,NaN和NaN虽然不相等,但是在Map中视为同一个键)。
Map的遍历
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员。
一样的,Map遍历的顺序也是插入的顺序。1
2
3
4
5
6let m = new Map();
m.set('a',500);
m.set('b',600);
m.forEach( e => {
console.log(e); // 500 600 遍历顺序就是插入顺序
}一样的,使用…扩展运算符或者Array.from可以将Map转换为数组
1
2
3
4
5
6let m = new Map();
m.set('a',500);
m.set('b',600);
// let arr = [...m];
let arr = Array.from(m);
log(arr); //[ [ 'a', 500 ], [ 'b', 600 ] ]
Proxy
let p = new Proxy(target, handler);
如上,Proxy也是一个构造函数,接受两个对象作为参数,target为要进行代理的目标对象,handler也为对象,是一个配置对象,对于每一个要代理的操作,提供一个方法拦截对应的请求。
先来理解代理:对target目标对象的访问,都设置一层拦截层,可以对外界的操作进行过滤和改写,这就是代理Proxy。
先来看一个例子:
1 |
let targetObj = {}; // 目标对象 |
上述例子中:通过给target设置代理,通过Proxy的实力访问目标对象时,会执行我们在handler中规定的方法。
需要注意的是: 一定要通过Proxy实例来访问,才会通过代理。