ES6语法笔记(Symbol、Set、Map、Proxy、Reflect、Promise)
**以下内容均摘自ECMAScript 6 入门——阮一峰
一、Symbol数据类型(undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)、Symbol)
1.Symbol()表示一个独一无二的值,接受一个参数作为该值的描述。
// 没有参数的情况 let s1 = Symbol(); let s2 = Symbol(); s1 === s2 // false // 有参数的情况 let s1 = Symbol('foo'); let s2 = Symbol('foo'); s1 === s2 // false
Symbol值不能与其他类型的值计算 Symbol值可以转化为布尔值(true)
2.定义独一无二的属性名,防止被改写或覆盖
let mySymbol = Symbol(); // 第一种写法 let a = {}; a[mySymbol] = 'Hello!'; // 第二种写法 let a = { [mySymbol]: 'Hello!' };
3.Symbol的遍历
Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名
Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()、for...in、for...of均无法遍历到Symbol类型属性
4.使用Symbol.for()重新使用同一个 Symbol 值(没有则新建并返回一个以该字符串为名称的 Symbol 值)
Symbol.for("bar") === Symbol.for("bar") // true Symbol("bar") === Symbol("bar") // false
5.Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。
let s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" let s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined
二、Set数据结构类似于数组、但是成员的值都是唯一的,没有重复的值。
const set = new Set([1, 2, 3, 4, 4]); [...set] // [1, 2, 3, 4]
1.Set实例的属性和方法
Set.prototype.constructor:构造函数,默认就是Set函数。Set.prototype.size:返回Set实例的成员总数。add(value):添加某个值,返回 Set 结构本身。delete(value):删除某个值,返回一个布尔值,表示删除是否成功。has(value):返回一个布尔值,表示该值是否为Set的成员。clear():清除所有成员,没有返回值。
Array.from方法可以将 Set 结构转为数组。
const items = new Set([1, 2, 3, 4, 5]); const array = Array.from(items);
2.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"]
在遍历操作中,同步改变原来的 Set 结构
// 方法一 let set = new Set([1, 2, 3]); set = new Set([...set].map(val => val * 2)); // set的值是2, 4, 6 // 方法二 let set = new Set([1, 2, 3]); set = new Set(Array.from(set, val => val * 2)); // set的值是2, 4, 6
3.WeakSet 结构与 Set 类似、无法遍历、只能存放对象、且对象为弱引用(如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中)
三、Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。
const m = new Map(); const o = {p: 'Hello World'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
const map = new Map([ ['name', '张三'], ['title', 'Author'] ]); map.size // 2 map.has('name') // true map.get('name') // "张三" map.has('title') // true map.get('title') // "Author"
1.Map结构的属性和操作方法
- size 属性
- set(key, value)
- get(key)
- has(key)
- delete(key)
- clear()
keys():返回键名的遍历器。values():返回键值的遍历器。entries():返回所有成员的遍历器。forEach():遍历 Map 的所有成员。
2.与其他数据结构的相互转化
//Map 转为数组 const myMap = new Map() .set(true, 7) .set({foo: 3}, ['abc']); [...myMap] // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ] //数组 转为 Map new Map([ [true, 7], [{foo: 3}, ['abc']] ]) // Map { // true => 7, // Object {foo: 3} => ['abc'] // } //Map 转为对象 //如果所有 Map 的键都是字符串,它可以无损地转为对象。 //Map 转为 JSON //Map 的键名都是字符串,这时可以选择转为对象 JSON。 //Map 的键名有非字符串,这时可以选择转为数组 JSON。 //JSON 转为 Map同理
3.WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。键名所引用的对象都是弱引用。没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。无法清空,即不支持clear方法。因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。
四、Proxy
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
Proxy 实际上重载(overload)了点运算符,即用自己的定义覆盖了语言的原始定义。
//ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。 var proxy = new Proxy(target, handler);
1.Proxy 支持的拦截操作
get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。
var person = { name: "张三" }; var proxy = new Proxy(person, { get: function(target, property) { if (property in target) { return target[property]; } else { throw new ReferenceError("Property \"" + property + "\" does not exist."); } } }); proxy.name // "张三" proxy.age // 抛出一个错误
set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。
let validator = { set: function(obj, prop, value) { if (prop === 'age') { if (!Number.isInteger(value)) { throw new TypeError('The age is not an integer'); } if (value > 200) { throw new RangeError('The age seems invalid'); } } // 对于满足条件的 age 属性以及其他属性,直接保存 obj[prop] = value; } }; let person = new Proxy({}, validator); person.age = 100; person.age // 100 person.age = 'young' // 报错 person.age = 300 // 报错
apply(target, object, args):拦截 Proxy 实例作为函数调用的操作
var twice = { apply (target, ctx, args) { return Reflect.apply(...arguments) * 2; } }; function sum (left, right) { return left + right; }; var proxy = new Proxy(sum, twice); proxy(1, 2) // 6 proxy.call(null, 5, 6) // 22 proxy.apply(null, [7, 8]) // 30
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 // false
construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
var 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
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),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
2.this指向问题
在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。
const target = { m: function () { console.log(this === proxy); } }; const handler = {}; const proxy = new Proxy(target, handler); target.m() // false proxy.m() // true //this绑定原始对象,就可以解决这个问题。 const target = new Date('2015-01-01'); const handler = { get(target, prop) { if (prop === 'getDate') { return target.getDate.bind(target); } return Reflect.get(target, prop); } }; const proxy = new Proxy(target, handler); proxy.getDate() // 1
五、Reflect
将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
修改某些Object方法的返回结果,让其变得更合理。
让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。以下是Reflect对象的13个静态方法。
Reflect.apply(target, thisArg, args)
Reflect.construct(target, args)
Reflect.get(target, name, receiver)
Reflect.set(target, name, value, receiver)
Reflect.defineProperty(target, name, desc)
Reflect.deleteProperty(target, name)
Reflect.has(target, name)
Reflect.ownKeys(target)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, } Reflect.get(myObject, 'foo') // 1 Reflect.get(myObject, 'bar') // 2 Reflect.get(myObject, 'baz') // 3 //如果name属性部署了读取函数(getter),则读取函数的this绑定receiver。 var myObject = { foo: 1, bar: 2, get baz() { return this.foo + this.bar; }, }; var myReceiverObject = { foo: 4, bar: 4, }; Reflect.get(myObject, 'baz', myReceiverObject) // 8
//Reflect.ownKeys方法用于返回对象的所有属性,基本等同于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)]
六、Promise
1.Promise 是异步编程的一种解决方案。Promise 是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
它的缺点包括无法取消Promise,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
//回调错误函数为可选项 promise.then(function(value) { // success }, function(error) { // failure });
//Promise 新建后就会立即执行。 //then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。 let promise = new Promise(function(resolve, reject) { console.log('Promise'); resolve(); }); promise.then(function() { console.log('resolved.'); }); console.log('Hi!'); // Promise // Hi! // resolved
const getJSON = function(url) { const promise = new Promise(function(resolve, reject){ const handler = function() { if (this.readyState !== 4) { return; } if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } }; const client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler; client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); }); return promise; }; getJSON("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出错了', error); });
resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error)) // Error: fail
由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。
2.Promise.prototype.then()方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
//第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。 getJSON("/posts.json").then(function(json) { return json.post; }).then(function(post) { // ... });
3.Promise.prototype.catch()用于指定发生错误时的回调函数。如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获。
//第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误 // bad promise .then(function(data) { // success }, function(err) { // error }); // good promise .then(function(data) { //cb // success }) .catch(function(err) { // error });
如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。
4.Promise.prototype.finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
5.Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
//p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。 const p = Promise.all([p1, p2, p3]);
只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
// 生成一个Promise对象的数组 const promises = [2, 3, 5, 7, 11, 13].map(function (id) { return getJSON('/post/' + id + ".json"); }); Promise.all(promises).then(function (posts) { // ... }).catch(function(reason){ // ... });
如果作为参数的 Promise 实例,自己定义了catch方法,那么它一旦被rejected,并不会触发Promise.all()的catch方法。调动catch方法后实例会resolved,因此会调用all的
then方法指定的回调函数,而不会调用catch方法指定的回调函数。如果实例没有catch方法,则会调用all的catch方法。
6.Promise.race()同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
//只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。 const p = Promise.race([p1, p2, p3]);
7.Promise.resolve()将现有对象转为 Promise 对象
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。
如果参数是 thenable对象(指的是具有then方法的对象),Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。
let thenable = { then: function(resolve, reject) { resolve(42); } }; let p1 = Promise.resolve(thenable); p1.then(function(value) { console.log(value); // 42 });
如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved。
const p = Promise.resolve('Hello');
p.then(function (s){
console.log(s)
});
// Hello
如果调用时不带参数,直接返回一个resolved状态的 Promise 对象。
//setTimeout(fn, 0)在下一轮“事件循环”开始时执行,Promise.resolve()在本轮“事件循环”结束时执行,console.log('one')则是立即执行,因此最先输出。 setTimeout(function () { console.log('three'); }, 0); Promise.resolve().then(function () { console.log('two'); }); console.log('one'); // one // two // three
8.Promise.reject()方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。这一点与Promise.resolve方法不一致。

浙公网安备 33010602011771号