一、Symbol
Symbol是ES6引入的一种新的数据结构类型, 表示独一无二的值。是JavaScript的第七种数据类型,其他六种为:Undefined、 Null、 布尔值(Boolean)、 字符串(String)、 数值(Number)和对象(Object).ES5对象属性名都是字符串会造成属性名的冲突, Symbol能保证每个属性的名字都是独一无二的, 从根本上防止了属性名的冲突,这就是ES6引入Symbol的原因。
Symbol值通过Symbol()函数生成,该函数可以接受一个字符串作为参数,表示对Symbol示例的描述, 这样主要方便在控制台输出和转为字符串时比较容易区分。
var s = Symbol();
var s1 = Symbol('foo');
typeof s;//'symbol'
s1;//Symbol()
s2;//Symbol(foo)
s1.toString();//'Symbol("foo")'
注意几点:
- Symbol函数不能使用new命令,否则会报错。因为生成的Symbol是一个原始类型,不是对象,因此不能添加属性。它是一种类似于字符串的数据类型
- 其传入的参数只是对当前Symbol值的描述,因此相同参数的Symbol函数的返回值是不相等的
- Symbol值不能与其他类型的值进行运算
- Symbol值可以显示转为字符串和布尔值,但是不能转为数值
- *
var sym = Symbol('My Symbol');
String(sym);//Symbol(My Symbol)
var sym2 = Symbol();
Boolean(sym2);//true
!sym2 //false
1.Symbol值作为属性名
鉴于Symbol值独一无二的特性,可以作为标识符用于对象的属性名可以保证不会出现同名的属性。这样对象的属性名现在可以有两种数据类型:原来的字
符串类型和新增的Symbol类型。当Symbol值作为属性名时该属性是公开属性,不是私有属性。
Symbol值作为属性名有三种写法:
var mySymbol = Symbol();
//第一种写法
var a = {};
a[mySymbol] = 'hello';
//第二种写法
var a = {
[mySymbol] : 'hello'
}
//第三种写法
var a = {};
Object.defineProperty(a, mySymbol, {value:'hello'});
注意: Symbol值作为属性名不能使用点运算符,因此点运算符后面总是字符串,因此无法读取Symbol做标识的值,在对象内部必须使用[]包裹Symbol值来定义属性。如果不放在方括号中那么代表的就是一个字符串。方法也是如此,可以结合简洁表示法是代码变得简洁。
let s = Symbol();
let obj = {
[s](arg){...}
};
//等价于
let obj = {
[s]:function(arg){...}
}
obj[s](123);
另外Symbol值还可以定义一组常量,保证这组常量的值都是不相等的。这样做的最大好处是其他任何值不可能有相同的值,保证如下代码中switch语句可以按设计的方式工作。
const COLOR_RED = Symbol();
const COLOR_GREEN = Symbol();
function getColor(color){
switch(color){
case COLOR_RED:
return COLOR_GREEN;
case COLOR_GREEN:
return COLOR_RED;
}
}
2.属性名遍历
Symbol作为属性名,该属性不会出现在for…in、for…of循环中。也不会被Object.keys()、Object.getOwnPropertyNames()返回,有一个Object.getOwnPropertySymbols()方法可以获取指定对象的所有Symbol属性名,该方法返回一个数组,成员是当前对象的所有用作属性名的Symbol值。
var obj = {};
var a = Symbol('a');
var b = Symbol('b');
obj[a] = 'hello';
obj[b] = 'world';
var objectSymbols = Object.getOwnPropertySymbols(obj);//[Symbol(a), Symbol(b)]
另外Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和Symbol键名。利用Symbol不会被常规方法遍历到但是仍然是公开属性的特性可以为对象定义一些非私有但又希望只用于内部的方法。
3.Symbol.for()和Symbol.keyFor()
除了Symbol()之外还可以通过Symbol.for(),两者都可以生成新的Symbol。后者会被登记在全局环境中供搜索,而前者不会。因此Symbol.for()不会每次都返回一个新的Symbol类型的值,而是首先检查给定的key是否已经存在,如果不存在才会创建一个新值。而Symbol()没有登记机制,所以每次调用都会返回一个新的不同的Symbol值
Symbol('30'));//调用30次会返回30个不同的Symbol值
Symbol.for('30');//调用30次返回同一个Symbol值
Symbol.for()方法会创建一个登记的的Symbol值,Symbol.keyFor()返回一个已登记的Symbol类型值的key
4.内置的Symbol值
ES6提供了11个内置的Symbol值,指向语言内部使用的方法。
二、Proxy
Proxy译作’代理器’。在ES6中引入用来修改某些操作的默认行为,相当于在原始操作和访问之间加了一个中间层可以对外界的访问进行过滤和改写(类比代理模式)。
ES6提供Proxy构造函数用来生成Proxy实例。
//target表示拦截的目标对象,handler参数也是一个对象用来表示拦截行为。
var proxy = new Proxy(target, handler);
//使用示例,配置对象有一个get方法用来拦截对目标对象属性的访问请求,get方法的两个参数分别是要拦截的对象和要访问的属性,由于拦截对象中get返回35,因此访问
//目标对象的任何属性都将得到35.如果handler没有任何拦截那就等同于直接通向原对象。
var proxy = new Proxy({}, {
get: function(target, propetry){
return 35;
}
}
);
proxy.time;//35
proxy.name;//35
proxy.title;//35
注意要是Proxy起作用必须针对Proxy操作而不是针对目标对象操作,我们可以将Proxy设置到object.proxy属性上从而可以在object对象上调用
1.Proxy支持的拦截操作
- get():用于拦截某个属性的读取操作
- set():用于拦截某个属性的赋值操作
- apply():拦截函数的调用、call和apply操作
- has():has方法可以用来隐藏某些属性不被in操作符发现
- deleteProperty(): 拦截删除操作,如果这个方法抛出错误或者返回false则当前属性就无法被删除
- defineProperty(): 拦截了Object.defineProperty操作,可以导致添加新属性失败
- enumerate(): 用于拦截for…in循环。注意与has的区别。后者用于拦截in操作符,对for…in循环无效
- getOwnPropertyDescription():拦截Object.getOwnPropertyDescriptor,返回一个属性描述对象或者undefined
- getPropertyOf(): 用来拦截Object.getPropertyOf()运算符及其他一些操作
- ownKeys(): 用于拦截Object.keys()操作
- preventExtensions(): 用于拦截Object.preventExtensions().该方法必须返回一个布尔值
Proxy.revocable()
该方法返回一个可取消的Proxy实例.
let target = {};
let hanlder = {};
//返回一个对象,其proxy属性是Proxy实例,revoke是一个函数可以取消proxy实例。
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo;//123
revoke();
proxy.foo;//TypeError:Revoked
三、Reflect
Reflect是ES6为了操作对象而提供的新的API。其目的如下:
- 将Object对象的一些属于语言层面的方法放到Reflect对象上,未来的新方法将会只部署到Reflect对象上
- 修改某些Object方法的返回结果让其更合理
- 让Object操作都变成函数行为
- Reflect对象的方法与Proxy对象的方法一一对应。可以让Proxy对象方便的调用对应的Reflect方法完成默认行为
关于Reflect的方法这里不再赘述,参考Reflect API。
以上就是关于Symbol,Proxy,Reflect三种新的API的介绍