Set 和 Map 数据结构
1,Set:类似于数组,但是成员都是唯一的,可以用来去重
增加变量的方法: add,在set看来两个对象都是不相等的, delete(value)删除某个值 has(value)判断有没有某个值 clear(value)清除整个set对象
数组与set互相转换: const set = new Set(xxx数组)和[...xxxSet],数组还可以用Array.from()方法将set转为数组
set的遍历: keys() values() entries() forEach() ,由于set没有键值所以keys和values方法的返回值一样,这样entries方法返回的键值和值是一样的
2,WeakSet
weakset与set类似,只有两个区别,一个是weakset只能存放对象,另一个是WeakSet引用都是弱引用,假如对象只是在WeakSet中引用,那么垃圾回收机制将会自动回收此对象,所以它没有遍历方法,因为它的对象随时都可能消失
语法:const ws = new WeakSet();
3,Map
题外话: forEach遍历对象(Obj.forEach(([key, value]) => {}))
构造函数: 任何具有Iterator 接口、且每个成员都是一个双元素的数组结构都可以构成一个Map对象
属性与方法: size(成员数量),set(新增成员),get(用key获键值),has(判断key是否存在),delete(删除key),clear(清空成员)
数据结构的转换: [...Map](Map转为数组),new Map(Array)(数组转为Map)
4,WeakMap
WeakMap与Map的区别有两点,一是WeakMap只接受对象作为键,其次,WeakMap
的键名所指向的对象,不计入垃圾回收机制。
Proxy
1,概述
Proxy用于修改某些默认操作的默认行为,可以理解成在目标对象之前架设一层拦截,外界在访问该对象时,都必须经过这层拦截
var obj = new Proxy({}, {
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
}
});
注: 要使得Proxy
起作用,必须针对Proxy
实例(上例是proxy
对象)进行操作,而不是针对目标对象(上例是空对象)进行操作,如果handle没有设置任何拦截,那就等同于直接通向原对象。
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
2,Proxy的方法
get:拦截属性的读取操作,三个参数,依次是目标对象(必选), 属性名(必选),proxy实力本身(非必选)
题外话: 匿名函数的写法有: var niminghanshu = (function(x , y){})(这里传参)
set:拦截属性的赋值操作,四个参数,依次是目标对象(必选), 属性名(必选),属性值(必选), proxy实力本身(非必选)
apply:拦截函数的调用、call和apply操作,三个操作,依次是目标对象、目标对象的上下文对象(this)、目标对象的参数数组
has:拦截Hasproperty操作,即判断对象是否具有某个属性时,这个方法会生效,两个参数,依次是目标对象、需查询的属性名
construct:拦截new命令,两个参数,依次是目标对象,参数对象,拦截必须返回一个对象,否则会报错
deleteProperty: 拦截delete操作,如果拦截返回错误或false,那么这个属性就不会删除
defineProperty: 拦截Object.defineProperty操作,如果拦截返回错误或false,那么这个属性就不会添加
var handler = {
defineProperty (target, key, descriptor) {
return false;
}
};
var target = {};
var proxy = new Proxy(target, handler);
proxy.foo = 'bar' // 不会生效
getOwnPropertyDescriptor: getOwnPropertyDescriptor
方法拦截Object.getOwnPropertyDescriptor()
,返回一个属性描述对象或者undefined
getPrototypeOf: getPrototypeOf
方法主要用来拦截获取对象原型
Object.prototype.__proto__
Object.prototype.isPrototypeOf()
Object.getPrototypeOf()
Reflect.getPrototypeOf()
instanceof
isExtensible: isExtensible
方法拦截Object.isExtensible
操作,注意,该方法只能返回布尔值,否则返回值会被自动转为布尔值这个方法有一个强限制,它的返回值必须与目标对象的isExtensible
属性保持一致,否则就会抛出错误
var p = new Proxy({}, {
isExtensible: function(target) {
return false;
}
});
Object.isExtensible(p) // 报错,因为它确实是true
ownKeys: ownKeys
方法用来拦截对象自身属性的读取操作
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Object.keys()
for...in
循环
注意,使用Object.keys
方法时,有三类属性会被ownKeys
方法自动过滤,不会返回
- 目标对象上不存在的属性
- 属性名为 Symbol 值
- 不可遍历(
enumerable
)的属性
preventExtensions:拦截Object.preventExtensions()
。该方法必须返回一个布尔值,否则会被自动转为布尔值
setPrototypeOf:拦截Object.setPrototypeOf
方法,注意,该方法只能返回布尔值,否则会被自动转为布尔值。另外,如果目标对象不可扩展(extensible),setPrototypeOf
方法不得改变目标对象的原型
3,Proxy.revocable
Proxy.revocable
方法返回一个可取消的 Proxy 实例
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler); // 返回的是一个对象,对象的proxy属性就是proxy对象,revoke属性就是一个可以取消proxy函数
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Proxy.revocable
的使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问
4,this 问题
在 Proxy 代理的情况下,目标对象内部的this
关键字会指向 Proxy 代理。
Reflect
1,概述
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API,他的目的有:(1)将object对象一些明显属于语言内部的方法放到reflect上面,(2)修改object的一些方法结果让其变得合理,比如有的方法出错时会抛出异常,但是使用reflect的这个方法会返回Boolean数值,(3)让object操作都变成函数行为,有的操作之前是命令式的。比如delete xxx,现在改成Reflect.deleteProperty(obj, name),(4)reflect对象方法与proxy对象的方法都是一一对应的,这样在proxy函数中就算丢失this指向问题也可以用reflect来指向原对象,且proxy的拦截对reflect不起作用
Proxy(target, {
set: function(target, name, value, receiver) {
var success = Reflect.set(target,name, value, receiver); // 虽然被proxy拦截,但是利用reflect还是可以达到之前的默认效果
if (success) {
log('property ' + name + ' on ' + target + ' set to ' + value);
}
return success;
}
});
2,静态方法
reflect的静态方法与proxy的静态方法一致,只是功能有偏差
3,实例:使用 Proxy 实现观察者模式
利用proxy的拦截和reflect的代理默认方法,在拦截set的时候可以在完成默认事件之后完成自己的操作,这就起到了观察的作用
Promise 对象
1,Promise 的含义
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。promise对象的两个特点
(1)对象的状态不受外界影响,promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败)(2)一旦改变状态就不会改变
2,基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
resolve函数将pending变成resolved并将参数带出去,reject函数将pending变成rejected并将参数带出去,promise实例生成以后可以用then方法指定resolved状态和rejected状态的回调函数。Promise 新建后就会立即执行,但是他的resolve方法和reject方法只会在本轮结束才执行。假如一个promise套另外的promise,那么只有里面的promise的状态完结之后外面的promise后续才会执行。注意:resolve和reject方法都不会结束promise的执行,假如在这两个方法之后还有其它语句,则还是会执行,所以一般是return resolve()这样的写法。
3,Promise.prototype.then
then是promise实例的状态改变时的回调函数,他的第一个参数是resolved状态的回调函数,为必选,第二个参数是rejected状态的回调函数,为非必选。then方法返回的是新promise实例,所以此写法为链式写法
4,Promise.prototype.catch
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数。
5,Promise.prototype.finally
finally
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
6,Promise.all
Promise.all
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。例:const p = Promise.all([p1, p2, p3]);
只有3个全变成resolved,总的才会变成resolved,只要有一个是rejected,总的就会是rejected。假如每个promise都有catch,那么总的promise的catch就不会捕获。
7,Promise.race
所有promise组成一个promise,谁先改变那么总的promise就随着它改变
8,Promise.resolve
作用:将现有对象转为 Promise 对象。参数的四种情况:(1)参数是promise实例,此方法将原封不动的返回此对象,(2)参数是一个thenable对象,比如:
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
那么此对象转成promise对象时会变成resolved状态,且立即调用then回调函数,(3)参数不是具有then方法的对象或根本就不是对象,调用此方法后会立即变成一个状态为resolved的promise对象且将此参数作为调用回调函数中的参数,(4)没有参数,直接立即变成一个状态为resolved的promise对象且调用回调函数
9,Promise.reject
与resolve方法用法相同,只是返回的是rejected状态的Promise实例,其次参数是thenable对象时reject方法返回的就是此thenable对象
10,Promise.try
有时为了让promise中的函数立即调用,promise.try()方法就是让promise中的回调函数立即执行,不会在本轮结束后才执行
Promise.try(database.users.get({id: userId}))
.then(...)
.catch(...)
Iterator 和 for...of 循环
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator
属性,或者说,一个数据结构只要具有Symbol.iterator
属性,就可以认为是“可遍历的”(iterable)。Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名Symbol.iterator
,它是一个表达式,返回Symbol
对象的iterator
属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内,放在点号之后会被看做字符串
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
}
};
原生具备 Iterator 接口的数据结构有:Array,Map,Set,String,TypedArray,函数的arguments对象,NodeList
调用Iterator接口的场合:解构赋值、扩展运算符、yield*、数组的遍历
Iterator接口和Generator函数:利用generator的field声明来实现next方法
return和throw方法:用到return方法的时候是当循环报错或自动break时。
for...in和for...of的区别:for...in遍历数组的键名是数字,但是for...in
循环是以字符串作为键名“0”、“1”、“2”,for...in
循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
Generator 函数
1,简介
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态。yield
表达式是暂停执行的标记,而next
方法可以恢复执行
2,yield 表达式
yield
表达式就是暂停标志。
遍历器对象的next
方法的运行逻辑如下。
(1)遇到yield
表达式,就暂停执行后面的操作,并将紧跟在yield
后面的那个表达式的值,作为返回的对象的value
属性值。
(2)下一次调用next
方法时,再继续往下执行,直到遇到下一个yield
表达式。
(3)如果没有再遇到新的yield
表达式,就一直运行到函数结束,直到return
语句为止,并将return
语句后面的表达式的值,作为返回的对象的value
属性值。
(4)如果该函数没有return
语句,则返回的对象的value
属性值为undefined
。
另外,yield
表达式如果用在另一个表达式之中,必须放在圆括号里面。
3,next 方法的参数
yield
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
4,Generator.prototype.throw
Generator 函数返回的遍历器对象,都有一个throw
方法,可以在函数体外抛出错误,然后在 Generator 函数体内捕获。但是只能捕获一次。throw
方法抛出的错误要被内部捕获,前提是必须至少执行过一次next
方法。throw
方法被捕获以后,会附带执行下一条yield
表达式。也就是说,会附带执行一次next
方法。另外,throw
命令与g.throw
方法是无关的,两者互不影响。
5,Generator.prototype.return
Generator 函数返回的遍历器对象,还有一个return
方法,可以返回给定的值,并且终结遍历 Generator 函数。
6,yield* 表达式
一个generator函数中调用另一个generator函数的时候需要加上yield*前缀
async 函数
1,含义
async 函数是 Generator 函数的语法糖。async
函数就是将 Generator 函数的星号(*
)替换成async
,将yield
替换成await。
async
函数对 Generator 函数的改进,体现在以下四点:
(1)内置执行器;async
函数的执行,与普通函数一模一样,只要一行
(2)更好的语义:async
和await
,比起星号和yield
,语义更清楚了。async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果
(3)更广的适用性(4)返回值是 Promise