ECMAScript学习(三)ES的新特性

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.fooproxy[‘foo’]
  • 2.set(target, propKey, value, receiver)
    拦截对象属性的设置,比如proxy.foo = vproxy[‘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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值