一.Symbol:
1.原始数据类型Symbol:
(1)表示独一无二的值。它是 JavaScript 语言的第七种数据类型,
前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)
(2)Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分
let s1 = Symbol('foo');
let s2 = Symbol('bar');
s1 // Symbol(foo)
s2 // Symbol(bar)
s1.toString() // "Symbol(foo)"
s2.toString() // "Symbol(bar)"
2.作为属性名的Symbol:
(1)每一个 Symbol 值都是不相等的,这意味着 Symbol 值可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性
let mySymbol = Symbol();
let a = {};
a[mySymbol] = 'Hello!';
(2)常量使用 Symbol 值最大的好处,就是其他任何值都不可能有相同的值了,因此可以保证上面的switch语句会按设计的方式工作
(3)Symbol 值作为属性名时,该属性还是公开属性,不是私有属性
3.消除魔术字符串:
(1)魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值
(2)常用的消除魔术字符串的方法,就是把它写成一个变量;
这时改用 Symbol 值最好
4.属性名的遍历:
(1)Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名;
Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值
(2)Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名;
5.Symbol.for(),Symbol.keyFor():
(1)Symbol.for()方法,它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。
如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值
(2)Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
6.模块的 Singleton 模式:
(1)Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例
(2)键名使用Symbol方法生成,那么外部将无法引用这个值,当然也就无法改写
7.内置的 Symbol 值:
(1)Symbol.hasInstance:
对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。
比如,foo instanceof Foo在语言内部,实际调用的是FooSymbol.hasInstance
(2)Symbol.isConcatSpreadable:
对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开
(3)Symbol.species:
对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性
(4)Symbol.match:
对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值
(5)Symbol.replace:
对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值
(6)Symbol.search:
对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值
(7)Symbol.split:
对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值
(8)Symbol.iterator:
对象的Symbol.iterator属性,指向该对象的默认遍历器方法
(9)Symbol.toPrimitive:
对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。
Number:该场合需要转成数值
String:该场合需要转成字符串
Default:该场合可以转成数值,也可以转成字符串
(10)Symbol.toStringTag:
对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。
也就是说,这个属性可以用来定制[object Object]或[object Array]中object后面的那个字符串
(11)Symbol.unscopables:
对象的Symbol.unscopables属性,指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除
二.Set和Map数据结构:
1.Set:
(1)ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
(2)Set 本身是一个构造函数,用来生成 Set 数据结构
(3)Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
(4)一种去除数组重复成员的方法:
// 去除数组的重复成员
[...new Set(array)]
(5)Set 结构的实例有以下属性:
Set.prototype.constructor:构造函数,默认就是Set函数;
Set.prototype.size:返回Set实例的成员总数
(6)Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
操作方法:
add(value):添加某个值,返回 Set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值
代码示例:
s.add(1).add(2).add(2);
// 注意2被加入了两次
s.size // 2
s.has(1) // true
s.has(2) // true
s.has(3) // false
s.delete(2);
s.has(2) // false
(7)Array.from方法可以将 Set 结构转为数组;这就提供了去除数组重复成员的另一种方法
function dedupe(array) {
return Array.from(new Set(array));
}
dedupe([1, 1, 2, 3]) // [1, 2, 3]
(8)Set 结构的实例有四个遍历方法,可以用于遍历成员:
keys():返回键名的遍历器
values():返回键值的遍历器
entries():返回键值对的遍历器
forEach():使用回调函数遍历每个成员
代码示例:
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
2.WeakSet:
(1)WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
首先,WeakSet 的成员只能是对象,而不能是其他类型的值;
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
(2)ES6 规定 WeakSet 不可遍历,没有size属性
(3)WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构
(4)WeakSet 结构有以下三个方法:
WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
3.Map:
(1)Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
(2)Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
(3)Set和Map都可以用来生成新的 Map:
代码示例:
const set = new Set([
['foo', 1],
['bar', 2]
]);
const m1 = new Map(set);
m1.get('foo') // 1
const m2 = new Map([['baz', 3]]);
const m3 = new Map(m2);
m3.get('baz') // 3
(4)Map 结构的实例有以下属性和操作方法:
a.size 属性:
size属性返回 Map 结构的成员总数
b.set(key, value):
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键
c.get(key):
get方法读取key对应的键值,如果找不到key,返回undefined
d.has(key):
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中
e.delete(key):
delete方法删除某个键,返回true。如果删除失败,返回false
f.clear():
clear方法清除所有成员,没有返回值
(5)Map 结构原生提供三个遍历器生成函数和一个遍历方法:
keys():返回键名的遍历器。
values():返回键值的遍历器。
entries():返回所有成员的遍历器。
forEach():遍历 Map 的所有成员
(6)Map 结构转为数组结构,比较快速的方法是使用扩展运算符(…)
代码示例:
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
(7)与其他数据结构的互相转换 :
a.Map 转为数组:
使用扩展运算符(…)
b.数组 转为 Map:
将数组传入 Map 构造函数,就可以转为 Map
c.Map 转为对象:
如果所有 Map 的键都是字符串,它可以无损地转为对象
d.对象转为 Map
e.Map 转为 JSON:
Map 的键名都是字符串,这时可以选择转为对象 JSON;
Map 的键名有非字符串,这时可以选择转为数组 JSON
f.JSON 转为 Map:
JSON 转为 Map,正常情况下,所有键名都是字符串
4.WeakMap:
(1)WeakMap结构与Map结构类似,也是用于生成键值对的集合
(2)WeakMap与Map的区别有两点:
首先,WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名;
其次,WeakMap的键名所指向的对象,不计入垃圾回收机制
(3)WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏
(4)WeakMap 与 Map 在 API 上的区别主要是两个:
一是没有遍历操作
二是无法清空,即不支持clear方法
(5)WeakMap只有四个方法可用:get()、set()、has()、delete()
(6)WeakMap 应用的典型场合就是 DOM 节点作为键名;
WeakMap 的另一个用处是部署私有属性
三.Proxy:
1.含义:
(1)Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程
(2)Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
(3)ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例:
var proxy = new Proxy(target, handler);
target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为
2.Proxy 实例的方法:
(1)get():
get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
(2)set():
set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选
(3)apply():
apply方法拦截函数的调用、call和apply操作。
apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组
(4)has():
has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。
has方法可以接受两个参数,分别是目标对象、需查询的属性名
(5)construct():
construct方法用于拦截new命令;
construct方法可以接受两个参数:
target:目标对象
args:构造函数的参数对象
newTarget:创造实例对象时,new命令作用的构造函数
(6)deleteProperty():
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除
(7)defineProperty():
defineProperty方法拦截了Object.defineProperty操作
(8)getOwnPropertyDescriptor():
getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined
(9)getPrototypeOf() :
getPrototypeOf方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
(10)isExtensible():
isExtensible方法拦截Object.isExtensible操作
(11)ownKeys():
ownKeys方法用来拦截对象自身属性的读取操作。具体来说,拦截以下操作。
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in循环
(12)preventExtensions():
preventExtensions方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值
(13)setPrototypeOf():
setPrototypeOf方法主要用来拦截Object.setPrototypeOf方法
3.Proxy.revocable():
Proxy.revocable方法返回一个可取消的 Proxy 实例
4.实例:Web 服务的客户端
Proxy 对象可以拦截目标对象的任意属性,这使得它很合适用来写 Web 服务的客户端
四.Reflect:
1.含义:
(1)Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API
(2)Reflect对象的设计目的有这样几个:
a.将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法
b.修改某些Object方法的返回结果,让其变得更合理
c.让Object操作都变成函数行为
d.Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法
2.静态方法:
(1)Reflect.get(target, name, receiver):
Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined
(2)Reflect.set(target, name, value, receiver):
Reflect.set方法设置target对象的name属性等于value
(3)Reflect.has(obj, name):
Reflect.has方法对应name in obj里面的in运算符
(4)Reflect.deleteProperty(obj, name):
Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性
(5)Reflect.construct(target, args):
Reflect.construct方法等同于new target(…args),这提供了一种不使用new,来调用构造函数的方法
(6)Reflect.getPrototypeOf(obj) :
Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)
(7)Reflect.setPrototypeOf(obj, newProto) :
Reflect.setPrototypeOf方法用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功
(8)Reflect.apply(func, thisArg, args):
Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数
(9)Reflect.defineProperty(target, propertyKey, attributes):
Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性
(10)Reflect.getOwnPropertyDescriptor(target, propertyKey) :
Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者
(11)Reflect.isExtensible (target):
Reflect.isExtensible方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。
(12)Reflect.preventExtensions(target):
Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功
(13)Reflect.ownKeys (target):
Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和