1、Promise
1.1、概念:
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
1.2、定义
new Promise(
/* executor */
function(resolve, reject) { ... }
);
1.3 promise特点
Promise对象有以下两个特点。
-
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
-
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
缺点:
Promise也有一些缺点。
-
1.首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
-
2.如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署Promise更好的选择。
1.4 三个状态
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。
1.5基本用法
1.5.1 new promise
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
1.5.2 promise.then
promise.then(function(value) {
// success
}, function(error) {
// failure
});
promise新建就会立即执行,例如:
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve();
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise
// Hi!
// resolved
注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
1.5.3 promise.catch()
promise.then(result => {···})
.catch(error => {···})
1.5.4 promise.finally
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
1.5.5、Promise.all()
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
-
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
-
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
1.5.6 Promise.race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
1.5.7 Promise.any()
Promise.any()跟Promise.race()方法很像,只有一点不同,就是不会因为某个 Promise 变成rejected状态而结束。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
console.log(first);
} catch (error) {
console.log(error);
}
上面代码中,Promise.any()方法的参数数组包含三个 Promise 操作。其中只要有一个变成fulfilled,Promise.any()返回的 Promise 对象就变成fulfilled。如果所有三个操作都变成rejected,那么await命令就会抛出错误。
1.5.8 Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
const jsPromise = Promise.resolve($.ajax('/whatever.json'));
1.5.9 Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log(s)
});
// 出错了
1.6 应用
异步请求一张网络图片
let data = 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1527252256191&di=7b25587cff2e776e89552261e850e286&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201504%2F12%2F20150412H1301_ZH4Mw.jpeg';
console.log('promise前面');
// 异步
let p = new Promise(function(resolve, reject) {
let img = new Image(); // 创建一个图片
img.src = data; // 给这个图片添加一个地址
// 请求成功了
img.onload = function() {
resolve(this);
};
// 请求失败了
img.onerror = function() {
reject('图片请求失败了');
};
console.log('promise本身的构造函数是一个同步')
});
console.log('promise中间');
// 成功了
p.then(function(data) {
// console.log(data);
document.body.appendChild(data);
console.log('promise执行成功');
});
// 失败执行
p.catch(function(err) {
console.log(err);
console.log('promise执行失败');
});
console.log('promise后面');
图片请求成功会在浏览器上显示并且打印结果:
promise前面
promise本身的构造函数是一个同步
promise中间
promise后面
promise执行成功
1.7、手动实现promise
function myPromise(constructor){
let self=this;
self.status="pending" //定义状态改变前的初始状态
self.value=undefined;//定义状态为resolved的时候的状态
self.reason=undefined;//定义状态为rejected的时候的状态
function resolve(value){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.value=value;
self.status="resolved";
}
}
function reject(reason){
//两个==="pending",保证了状态的改变是不可逆的
if(self.status==="pending"){
self.reason=reason;
self.status="rejected";
}
}
//捕获构造异常
try{
constructor(resolve,reject);
}catch(e){
reject(e);
}
}
// 定义链式调用的then方法
myPromise.prototype.then=function(onFullfilled,onRejected){
let self=this;
switch(self.status){
case "resolved":
onFullfilled(self.value);
break;
case "rejected":
onRejected(self.reason);
break;
default:
}
}
2、扩展运算符
扩展运算符(spread)是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
… 运算符主要用于数组和对象的一些操作。
函数调用
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
3、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
3.1、set的方法
let set = new Set();
set.add({});
set.size // 1
set.add({});
set.size // 2
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
Array.from方法可以将 Set 结构转为数组。
const items = new Set([1, 2, 3, 4, 5]);
const array = Array.from(items);
3.2 遍历方法
Set 结构的实例有四个遍历方法,可以用于遍历成员。
- Set.prototype.keys():返回键名的遍历器
- Set.prototype.values():返回键值的遍历器
- Set.prototype.entries():返回键值对的遍历器
- Set.prototype.forEach():使用回调函数遍历每个成员
3.3 weakset
- 首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。
3.3.1 weakset方法
WeakSet 结构有以下三个方法。
- WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
- WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
- WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在
4、Map
4.1、含义和基本用法
JavaScript 的对象(Object),本质上是键值对的集合(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
4.2、WeakMap
WeakMap 相对于普通的 Map,也是键值对集合,只不过 WeakMap 的 key 只能是非空对象(non-null object)。WeakMap 对它的 key 仅保持弱引用,也就是说它不阻止垃圾回收器回收它所引用的 key。WeakMap 最大的好处是可以避免内存泄漏。一个仅被 WeakMap 作为 key 而引用的对象,会被垃圾回收器回收掉。
WeakMap 拥有和 Map 类似的 set(key, value) 、get(key)、has(key)、delete(key) 和 clear() 方法,但没有 size 属性,也没有任何与迭代有关的方法。
为了演示 WeakMap 与内存回收的关系,我用 IE11 做了一个简单的测试。IE11 的 F12 开发者工具改进很大,下次找机会单独介绍。测试流程如下:
- 1.创建一个全局的 Map/WeakMap 对象;
- 2.进入局部作用域,创建大量对象作为 key,加到 Map/WeakMap 中;
- 3.离开局部作用域,检查第 2 步创建的大量对象是否被回收;
- 4.手动回收 Map/WeakMap 对象;
内存使用结果如下
]
5、模版字符串
模板字面量 是允许嵌入表达式的字符串字面量。你可以使用多行字符串和字符串插值功能。它们在ES2015规范的先前版本中被称为“模板字符串”。
当做引号使用,返回字符串
`aaaaa`
//返回字符串"aaaaa"
插入表达式,其实有点格式化输出的感觉
var a=123;
`aaa${a}aaa`
//返回字符串"aaa123aaa"
6、模块化
6.1、概念
模块是实现特定功能的一组属性和方法的封装。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
function m1() {//...}
function m2() { //...}
上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
这种做法的缺点很明显:”污染”了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。
为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。
6.2、对象
const module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
上面的函数m1和m2,都封装在module1对象里。使用的时候,就是调用这个对象的属性。
module1.m1();
但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
module1._count = 5;
7、Proxy代理
7.1 概念
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。
var proxy = new Proxy(target, handler);
7.2 拦截操作
下面是 Proxy 支持的拦截操作一览,一共 13 种。
- 1.get(target, propKey, receiver):
拦截对象属性的读取,比如proxy.foo和proxy[‘foo’]。 - 2.set(target, propKey, value, receiver):
拦截对象属性的设置,比如proxy.foo = v或proxy[‘foo’] = v,返回一个布尔值。 - 3.has(target, propKey):
拦截propKey in proxy的操作,返回一个布尔值。 - 4.deleteProperty(target, propKey):
拦截==delete proxy[propKey]==的操作,返回一个布尔值。 - 5.ownKeys(target):
拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for…in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而==Object.keys()==的返回结果仅包括目标对象自身的可遍历属性。 - 6.getOwnPropertyDescriptor(target, propKey):
拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。 - 7.defineProperty(target, propKey, propDesc):
拦截Object.defineProperty(proxy, propKey, propDesc)、Object.defineProperties(proxy, propDescs),返回一个布尔值。 - 8.preventExtensions(target):
拦截Object.preventExtensions(proxy),返回一个布尔值。 - 9.getPrototypeOf(target):
拦截Object.getPrototypeOf(proxy),返回一个对象。 - 10.isExtensible(target):
拦截Object.isExtensible(proxy),返回一个布尔值。 - 11.setPrototypeOf(target, proto):
拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。 - 12.apply(target, object, args):
拦截 Proxy 实例作为函数调用的操作,比如proxy(…args)、proxy.call(object, …args)、proxy.apply(…)。 - 13.construct(target, args):
拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(…args)。
8、Reflect反射
https://blog.youkuaiyun.com/WeiAiGeWangFeiYuQing/article/details/83043339