
ES6系列目录
- 1 let 和 const命令
- 2 变量的解构赋值
- 3 字符串的拓展
- 4 正则的拓展
- 5 数值的拓展
- 6 函数的拓展
- 7 数组的拓展
- 8 对象的拓展
- 9 Symbol
- 10 Set和Map数据结构
- 11 Proxy
- 12 Promise对象
- 13 Iterator和 for...of循环
- 14 Generator函数和应用
- 15 Class语法和继承
- 16 Module语法和加载实现
所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com
10 Set和Map数据结构
10.1 Set
介绍:
Set数据结构类似数组,但所有成员的值唯一。
Set本身为一个构造函数,用来生成 Set数据结构,使用 add方法来添加新成员。
-
let a = new Set(); -
[1,2,2,1,3,4,5,4,5].forEach(x=>a.add(x)); -
for(let k of a){ -
console.log(k) -
}; -
// 1 2 3 4 5
基础使用:
-
let a = new Set([1,2,3,3,4]); -
[...a]; // [1,2,3,4] -
a.size; // 4 -
// 数组去重 -
[...new Set([1,2,3,4,4,4])];// [1,2,3,4]
注意:
- 向
Set中添加值的时候,不会类型转换,即 5和 '5'是不同的。
-
[...new Set([5,'5'])]; // [5, "5"]
属性和方法:
属性:
-
Set.prototype.constructor:构造函数,默认就是 Set函数。 -
Set.prototype.size:返回 Set实例的成员总数。
操作方法:
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。
-
let a = new Set(); -
a.add(1).add(2); // a => Set(2) {1, 2} -
a.has(2); // true -
a.has(3); // false -
a.delete(2); // true a => Set(1) {1} -
a.clear(); // a => Set(0) {}
数组去重:
-
let a = new Set([1,2,3,3,3,3]);
10.2 Set的应用
数组去重:
-
// 方法1 -
[...new Set([1,2,3,4,4,4])]; // [1,2,3,4] -
// 方法2 -
Array.from(new Set([1,2,3,4,4,4])); // [1,2,3,4]
遍历和过滤:
-
let a = new Set([1,2,3,4]); -
// map 遍历操作 -
let b = new Set([...a].map(x =>x*2));// b => Set(4) {2,4,6,8} -
// filter 过滤操作 -
let c = new Set([...a].filter(x =>(x%2) == 0)); // b => Set(2) {2,4}
获取并集、交集和差集:
-
let a = new Set([1,2,3]); -
let b = new Set([4,3,2]); -
// 并集 -
let c1 = new Set([...a, ...b]); // Set {1,2,3,4} -
// 交集 -
let c2 = new Set([...a].filter(x => b.has(x))); // set {2,3} -
// 差集 -
let c3 = new Set([...a].filter(x => !b.has(x))); // set {1}
- 遍历方法:
-
keys():返回键名的遍历器。 -
values():返回键值的遍历器。 -
entries():返回键值对的遍历器。 -
forEach():使用回调函数遍历每个成员。
Set遍历顺序是插入顺序,当保存多个回调函数,只需按照顺序调用。但由于 Set结构没有键名只有键值,所以 keys()和 values()是返回结果相同。
-
let a = new Set(['a','b','c']); -
for(let i of a.keys()){console.log(i)}; // 'a' 'b' 'c' -
for(let i of a.values()){console.log(i)}; // 'a' 'b' 'c' -
for(let i of a.entries()){console.log(i)}; -
// ['a','a'] ['b','b'] ['c','c']
并且 还可以使用 for...of直接遍历 Set。
-
let a = new Set(['a','b','c']); -
for(let k of a){console.log(k)}; // 'a' 'b' 'c'
forEach与数组相同,对每个成员执行操作,且无返回值。
-
let a = new Set(['a','b','c']); -
a.forEach((v,k) => console.log(k + ' : ' + v));
10.3 Map
由于传统的 JavaScript对象只能用字符串当做键,给开发带来很大限制,ES6增加 Map数据结构,使得各种类型的值(包括对象)都可以作为键。
Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。基础使用:
-
let a = new Map(); -
let b = {name: 'leo' }; -
a.set(b,'my name'); // 添加值 -
a.get(b); // 获取值 -
a.size; // 获取总数 -
a.has(b); // 查询是否存在 -
a.delete(b); // 删除一个值 -
a.clear(); // 清空所有成员 无返回
注意:
- 传入数组作为参数,指定键值对的数组。
-
let a = new Map([ -
['name','leo'], -
['age',18] -
])
- 如果对同一个键多次赋值,后面的值将覆盖前面的值。
-
let a = new Map(); -
a.set(1,'aaa').set(1,'bbb'); -
a.get(1); // 'bbb'
- 如果读取一个未知的键,则返回
undefined。
-
new Map().get('abcdef'); // undefined
- 同样的值的两个实例,在 Map 结构中被视为两个键。
-
let a = new Map(); -
let a1 = ['aaa']; -
let a2 = ['aaa']; -
a.set(a1,111).set(a2,222); -
a.get(a1); // 111 -
a.get(a2); // 222
遍历方法: Map 的遍历顺序就是插入顺序。
-
keys():返回键名的遍历器。 -
values():返回键值的遍历器。 -
entries():返回所有成员的遍历器。 -
forEach():遍历 Map 的所有成员。
-
let a = new Map([ -
['name','leo'], -
['age',18] -
]) -
for (let i of a.keys()){...}; -
for (let i of a.values()){...}; -
for (let i of a.entries()){...}; -
a.forEach((v,k,m)=>{ -
console.log(`key:${k},value:${v},map:${m}`) -
})
将Map结构转成数组结构:
-
let a = new Map([ -
['name','leo'], -
['age',18] -
]) -
let a1 = [...a.keys()]; // a1 => ["name", "age"] -
let a2 = [...a.values()]; // a2 => ["leo", 18] -
let a3 = [...a.entries()];// a3 => [['name','leo'], ['age',18]]
10.4 Map与其他数据结构互相转换
- Map 转 数组
-
let a = new Map().set(true,1).set({f:2},['abc']); -
[...a]; // [[true:1], [ {f:2},['abc'] ]]
- 数组 转 Map
-
let a = [ ['name','leo'], [1, 'hi' ]] -
let b = new Map(a);
- Map 转 对象 如果所有 Map 的键都是字符串,它可以无损地转为对象。
如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
-
function fun(s) { -
let obj = Object.create(null); -
for (let [k,v] of s) { -
obj[k] = v; -
} -
return obj; -
} -
const a = new Map().set('yes', true).set('no', false); -
fun(a) -
// { yes: true, no: false }
- 对象 转 Map
-
function fun(obj) { -
let a = new Map(); -
for (let k of Object.keys(obj)) { -
a.set(k, obj[k]); -
} -
return a; -
} -
fun({yes: true, no: false}) -
// Map {"yes" => true, "no" => false}
- Map 转 JSON
(1)Map键名都是字符串,转为对象JSON:
-
function fun (s) { -
let obj = Object.create(null); -
for (let [k,v] of s) { -
obj[k] = v; -
} -
return JSON.stringify(obj) -
} -
let a = new Map().set('yes', true).set('no', false); -
fun(a); -
// '{"yes":true,"no":false}'
(2)Map键名有非字符串,转为数组JSON:
-
function fun (map) { -
return JSON.stringify([...map]); -
} -
let a = new Map().set(true, 7).set({foo: 3}, ['abc']); -
fun(a) -
// '[[true,7],[{"foo":3},["abc"]]]'
- JSON 转 Map
(1)所有键名都是字符串:
-
function fun (s) { -
let strMap = new Map(); -
for (let k of Object.keys(s)) { -
strMap.set(k, s[k]); -
} -
return strMap; -
return JSON.parse(strMap); -
} -
fun('{"yes": true, "no": false}') -
// Map {'yes' => true, 'no' => false}
(2)整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组:
-
function fun2(s) { -
return new Map(JSON.parse(s)); -
} -
fun2('[[true,7],[{"foo":3},["abc"]]]') -
// Map {true => 7, Object {foo: 3} => ['abc']}
11 Proxy
proxy 用于修改某些操作的默认行为,可以理解为一种拦截外界对目标对象访问的一种机制,从而对外界的访问进行过滤和修改,即代理某些操作,也称“代理器”。
11.1 基础使用
proxy实例化需要传入两个参数, target参数表示所要拦截的目标对象, handler参数也是一个对象,用来定制拦截行为。
-
let p = new Proxy(target, handler); -
let a = new Proxy({}, { -
get: function (target, handler){ -
return 'leo'; -
} -
}) -
a.name; // leo -
a.age; // leo -
a.abcd; // leo
上述 a实例中,在第二个参数中定义了 get方法,来拦截外界访问,并且 get方法接收两个参数,分别是目标对象和所要访问的属性,所以不管外部访问对象中任何属性都会执行 get方法返回 leo。
注意:
- 只能使用
Proxy实例的对象才能使用这些操作。 - 如果
handler没有设置拦截,则直接返回原对象。
-
let target = {}; -
let handler = {}; -
let p = new Proxy(target, handler); -
p.a = 'leo'; -
target.a; // 'leo'
同个拦截器函数,设置多个拦截操作:
-
let p = new Proxy(function(a, b){ -
return a + b; -
},{ -
get:function(){ -
return 'get方法'; -
}, -
apply:function(){ -
return 'apply方法'; -
} -
})
这里还有一个简单的案例:
-
let handler = { -
get : function (target, name){ -
return name in target ? target[name] : 16; -
} -
} -
let p = new Proxy ({}, handler); -
p.a = 1; -
console.log(p.a , p.b); -
// 1 16
这里因为 p.a=1 定义了 p中的 a属性,值为 1,而没有定义 b属性,所以 p.a会得到 1,而 p.b会得到 undefined从而使用 nameintarget?target[name]:16;返回的默认值 16;
Proxy支持的13种拦截操作:
13种拦截操作的详细介绍:打开阮一峰老师的链接。
-
get(target,propKey,receiver): 拦截对象属性的读取,比如proxy.foo和proxy['foo']。 -
set(target,propKey,value,receiver): 拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。 -
has(target,propKey): 拦截propKey in proxy的操作,返回一个布尔值。 -
deleteProperty(target,propKey): 拦截delete proxy[propKey]的操作,返回一个布尔值。 -
ownKeys(target): 拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。 -
getOwnPropertyDescriptor(target,propKey): 拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 -
defineProperty(target,propKey,propDesc): 拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 -
preventExtensions(target): 拦截Object.preventExtensions(proxy),返回一个布尔值。 -
getPrototypeOf(target): 拦截Object.getPrototypeOf(proxy),返回一个对象。 -
isExtensible(target): 拦截Object.isExtensible(proxy),返回一个布尔值。 -
setPrototypeOf(target,proto): 拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 -
apply(target,object,args): 拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。 -
construct(target,args): 拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
11.2 取消Proxy实例
使用 Proxy.revocale方法取消 Proxy实例。
-
let a = {}; -
let b = {}; -
let {proxy, revoke} = Proxy.revocale(a, b); -
proxy.name = 'leo'; // 'leo' -
revoke(); -
proxy.name; // TypeError: Revoked
11.3 实现 Web服务的客户端
-
const service = createWebService('http://le.com/data'); -
service.employees().than(json =>{ -
const employees = JSON.parse(json); -
}) -
function createWebService(url){ -
return new Proxy({}, { -
get(target, propKey, receiver{ -
return () => httpGet(url+'/'+propKey); -
}) -
}) -
}











