Symbol 数据类型
- ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它属于 JavaScript 语言的数据类型之一,其他数据类型是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、大整数(BigInt)、对象(Object)。
- 使用:通过
Symbol()函数生成 Symbol.prototype.description创建 Symbol 的时候,可以添加一个描述。const sym = Symbol('foo'); sym.description // "foo"
Set数据结构
- ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成 Set 数据结构
const s = new Set(); [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x)); for (let i of s) { console.log(i); } // 2 3 5 4 Set.prototype.size:返回Set实例的成员总数。Set.prototype.add(value):添加某个值,返回 Set 结构本身。Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。Set.prototype.clear():清除所有成员,没有返回值。Set.prototype.keys():返回键名的遍历器Set.prototype.values():返回键值的遍历器Set.prototype.entries():返回键值对的遍历器Set.prototype.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 //上面代码说明,forEach方法的参数就是一个处理函数。该函数的参数与数组的forEach一致,依次为键值、键名、集合本身(上例省略了该参数)。 //这里需要注意,Set 结构的键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的。另外,forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。
Map数据结构
- 它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
size属性返回 Map 结构的成员总数。Map.prototype.set(key, value)设置键名key对应的键值为value,然后返回整个 Map 结构Map.prototype.get(key)读取key对应的键值,如果找不到key,返回undefined。Map.prototype.has(key)方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。Map.prototype.delete(key)delete方法删除某个键,返回true。如果删除失败,返回false。Map.prototype.clear()clear方法清除所有成员,没有返回值。Map.prototype.keys():返回键名的遍历器。Map.prototype.values():返回键值的遍历器。Map.prototype.entries():返回所有成员的遍历器。Map.prototype.forEach():遍历 Map 的所有成员。const map = new Map([ ['F', 'no'], ['T', 'yes'], ]); for (let key of map.keys()) { console.log(key); } // "F" // "T" for (let value of map.values()) { console.log(value); } // "no" // "yes" for (let item of map.entries()) { // item是一个数组[key,value]形式 console.log(item[0], item[1]); } // "F" "no" // "T" "yes" // 或者 for (let [key, value] of map.entries()) { console.log(key, value); } // "F" "no" // "T" "yes" // 等同于使用map.entries() for (let [key, value] of map) { console.log(key, value); } // "F" "no" // "T" "yes"
Proxy用于修改某些操作的默认行为,
- 等同于在语言层面做出修改,与Object.defineProperties的行为大致一样(vue3的响应式数据就是基于Proxy写的)
// var proxy = new Proxy(target, handler); // 作为构造函数,Proxy接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。 var obj = new Proxy({}, { get: function (target, propKey, receiver) { console.log(`getting ${propKey}!`); return Reflect.get(target, propKey, receiver); }, set: function (target, propKey, value, receiver) { console.log(`setting ${propKey}!`); return Reflect.set(target, propKey, value, receiver); } }); - Proxy 支持的拦截操作一览。一共
13种。get(target, propKey, receiver):拦截对象属性的读取。参数分别为目标对象、属性名和 proxy 实例本身var person = { name: "张三" }; var proxy = new Proxy(person, { get: function(target, propKey) { if (propKey in target) { return target[propKey]; } else { throw new ReferenceError("Prop name \"" + propKey + "\" does not exist."); } } });set(target, propKey, value, receiver):拦截对象属性的设置,接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。可以接受两个参数,分别是目标对象、需查询的属性名。var handler = { has (target, key) { if (key[0] === '_') { return false; } return key in target; } }; var target = { _prop: 'foo', prop: 'foo' }; var proxy = new Proxy(target, handler); '_prop' in proxy // falsedeleteProperty(target, propKey):拦截delete操作,返回一个布尔值。(该方法解决了vue3中删除属性,不能响应式问题)var handler = { deleteProperty (target, key) { invariant(key, 'delete'); delete target[key]; return true; } }; function invariant (key, action) { if (key[0] === '_') { throw new Error(`Invalid attempt to ${action} private "${key}" property`); } } var target = { _prop: 'foo' }; var proxy = new Proxy(target, handler); delete proxy._prop // Error: Invalid attempt to delete private "_prop" propertyownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。var handler = { getOwnPropertyDescriptor (target, key) { if (key[0] === '_') { return; } return Object.getOwnPropertyDescriptor(target, key); } }; var target = { _foo: 'bar', baz: 'tar' }; var proxy = new Proxy(target, handler); Object.getOwnPropertyDescriptor(proxy, 'wat') // undefined Object.getOwnPropertyDescriptor(proxy, '_foo') // undefined Object.getOwnPropertyDescriptor(proxy, 'baz') // { value: 'tar', writable: true, enumerable: true, configurable: true }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,newTarget):construct()方法用于拦截new命令.方法可以接受三个参数。target:目标对象。args:构造函数的参数数组。newTarget:创造实例对象时,new命令作用的构造函数const p = new Proxy(function () {}, { construct: function(target, args) { console.log('called: ' + args.join(', ')); return { value: args[0] * 10 }; } }); (new p(1)).value // "called: 1" // 10
Reflect 。
- Reflect对象也是 ES6 新增的,意为"映射",表示把对象中常用的方法映射到Reflect对象上。所以只有静态方法。
- Reflect对象的设计目的
- 将
Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法 - 修改某些
Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。 - 让
Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
- 将
Reflect对象一共有 13 个静态方法,它与Proxy对象的方法是一一对应的Reflect.apply(target, thisArg, args)方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。Reflect.construct(target, args)方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。Reflect.get(target, name, receiver)方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。Reflect.set(target, name, value, receiver)方法设置target对象的name属性等于valueReflect.defineProperty(target, name, desc)方法基本等同于Object.defineProperty,用来为对象定义属性Reflect.deleteProperty(target, name)方法等同于delete obj[name],用于删除对象的属性Reflect.has(target, name)方法对应name in obj里面的in运算符Reflect.ownKeys(target)方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和。var myObject = { foo: 1, bar: 2, [Symbol.for('baz')]: 3, [Symbol.for('bing')]: 4, }; // 旧写法 Object.getOwnPropertyNames(myObject) // ['foo', 'bar'] Object.getOwnPropertySymbols(myObject) //[Symbol(baz), Symbol(bing)] // 新写法 Reflect.ownKeys(myObject) // ['foo', 'bar', Symbol(baz), Symbol(bing)]Reflect.isExtensible(target)方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。Reflect.preventExtensions(target)对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。Reflect.getOwnPropertyDescriptor(target, name)基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会替代掉后者。Reflect.getPrototypeOf(target)方法用于读取对象的__proto__属性Reflect.setPrototypeOf(target, prototype)方法用于设置目标对象的原型(prototype)
ES6新特性:Symbol数据类型与Set、Map及Proxy深入解析
1888





