ES6

Es6

1.数组

1.扩展运算符

是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列 。 主要用于函数调用。

functionadd(x,y) {

returnx+y;

}

constnumbers=[4,38];

add(...numbers)// 42

只有函数调用时,扩展运算符才可以放在圆括号中

(...[1,2])

// Uncaught SyntaxError: Unexpected number

console.log((...[1,2]))

// Uncaught SyntaxError: Unexpected number

console.log(...[1,2])

// 1 2

应用:

1.代替apply方法。

将一个数组添加到另一个数组的尾部。

// ES5的 写法

vararr1=[0,1,2];

vararr2=[3,4,5];

Array.prototype.push.apply(arr1,arr2);

// ES6 的写法

letarr1=[0,1,2];

letarr2=[3,4,5];

arr1.push(...arr2);

2.复制数组

const a2 = [...a1]

3.合并数组

[...a1,...a2,...a3]

4.与解构赋值结合

const[first,...rest]=[1,2,3,4,5];

first// 1

rest// [2, 3, 4, 5]

5.将字符串转换成数组

6.实现了iterator接口的对象

7.map,set结构和generator函数

2.Array.from()

将类数组对象和可遍历的对象转换成真正的对象

letarrayLike={

'0':'a',

'1':'b',

'2':'c',

length:3

};

// ES5的写法

vararr1=[].slice.call(arrayLike);// ['a', 'b', 'c']

// ES6的写法

letarr2=Array.from(arrayLike);// ['a', 'b', 'c']

3.Array.of

将一组值,转换为数组。

Array.of(3,11,8)// [3,11,8]

Array.of(3)// [3]

Array.of(3).length// 1

4.数组实例copyWithin()

在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组。

Array.prototype.copyWithin(target,start=0,end=this.length)

target(必需):从该位置开始替换数据。如果为负值,表示倒数。

start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。

end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算

// 将3号位复制到0号位

[1,2,3,4,5].copyWithin(0,3,4)

// [4, 2, 3, 4, 5]

5.数组实例find()和findIndex()

find()查找第一个符合条件的数组成员 ,找不到则返回undefined

[1,5,10,15].find(function(value,index,arr) {

returnvalue>9;

})// 10

findIndex() 返回第一个符合条件的数组成员的位置,找不到则返回-1

6.数组实例fill()

填充,第一个为 填充内容,第二个为开始位置,第三个为结束位置

['a','b','c'].fill(7,1,2)

// ['a', 7, 'c']

7.数组实例entries(),keys(),  values()

用于遍历数组,返回一个遍历器对象

8.数组实例includes()

方法返回一个布尔值,表示某个数组是否包含给定的值

[1,2,3].includes(2)// true

[1,2,3].includes(4)// false

[1,2,NaN].includes(NaN)// true

9.数组实例flat() 和flatMap()

用于拉平数组层级,参数为拉平的层级数

[1,2, [3, [4,5]]].flat()

// [1, 2, 3, [4, 5]]

[1,2, [3, [4,5]]].flat(2)

// [1, 2, 3, 4, 5]

10.数组的空位

forEach(), filter(), reduce(), every() 和some()都会跳过空位。

map()会跳过空位,但会保留这个值

join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

2.对象

1.与解构赋值

解构赋值必须是最后一个参数,否则会报错。

let{x,y,...z}={x:1,y:2,a:3,b:4};

x// 1

y// 2

z// { a: 3, b: 4 }

2.Object.is()

es5:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。  es6:不同之处只有两个:一是+0不等于-0,二是NaN等于自身。

3.Object.assign()

用于对象合并(实行的是浅拷贝,同名替换)

consttarget={a:1};

constsource1={b:2};

constsource2={c:3};

Object.assign(target,source1,source2);

target// {a:1, b:2, c:3}

3.symbol

一种新的原始数据类型Symbol,表示独一无二的值 。 Symbol函数前不能使用new命令 因为它是一个原始数据类型,注意:不能与其他类型的值进行运算

// 没有参数的情况

lets1=Symbol();

lets2=Symbol();

s1===s2// false

// 有参数的情况

lets1=Symbol('foo');

lets2=Symbol('foo');

s1===s2// false

当symbol作为属性名时,不能使用 点  运算符

const mySymbol = Symbol();

const a = {};

a.mySymbol = 'Hello!';

a[mySymbol] // undefined

a['mySymbol'] // "Hello!"

因为点运算符后面总是字符串,所以不会读取mySymbol作为标识名所指代的那个值,导致a的属性名实际上是一个字符串,而不是一个 Symbol 值。

1.description()

返回symbol的描述

const sym = Symbol('foo');

sym.description // "foo"

2.消除魔术字符串

魔术字符串指的是,在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值

function getArea(shape, options) {

  let area = 0;

  switch (shape) {

    case 'Triangle': // 魔术字符串

      area = .5 * options.width * options.height;

      break;

    /* ... more code ... */

  }

  return area;

}

getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

const shapeType = {

  triangle: Symbol()

};

function getArea(shape, options) {

  let area = 0;

  switch (shape) {

    case shapeType.triangle:

      area = .5 * options.width * options.height;

      break;

  }

  return area;

}

getArea(shapeType.triangle, { width: 100, height: 100 });

3.Symbol.for和Symbol.keyFor()

Symbol.for()与Symbol()这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30 次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30 次,会返回 30 个不同的 Symbol 值。

Symbol.keyFor()方法返回一个已登记的 Symbol 类型值的key。

let s1 = Symbol.for("foo");

Symbol.keyFor(s1) // "foo"

let s2 = Symbol("foo");

Symbol.keyFor(s2) // undefined

变量s2属于未登记的 Symbol 值,所以返回undefined。

4.内置的Symbol值

对象的Symbol.hasInstance属性,指向一个内部方法。当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法

对象的Symbol.isConcatSpreadable属性等于一个布尔值,表示该对象用于Array.prototype.concat()时,是否可以展开

let obj = {length: 2, 0: 'c', 1: 'd'};

['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']

obj[Symbol.isConcatSpreadable] = true;

['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']

对象的Symbol.species属性,指向一个构造函数。创建衍生对象时,会使用该属性。

对象的Symbol.match属性,指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值

对象的Symbol.replace属性,指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

对象的Symbol.search属性,指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

对象的Symbol.split属性,指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值

对象的Symbol.iterator属性,指向该对象的默认遍历器方法。

const myIterable = {};

myIterable[Symbol.iterator] = function* () {

  yield 1;

  yield 2;

  yield 3;

};

[...myIterable] // [1, 2, 3]

对象的Symbol.toPrimitive属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

Number:该场合需要转成数值

String:该场合需要转成字符串

Default:该场合可以转成数值,也可以转成字符串

let obj = {

  [Symbol.toPrimitive](hint) {

    switch (hint) {

      case 'number':

        return 123;

      case 'string':

        return 'str';

      case 'default':

        return 'default';

      default:

        throw new Error();

    }

  }

};

2 * obj // 246

3 + obj // '3default'

obj == 'default' // true

String(obj) // 'str'

10.对象的Symbol.toStringTag属性,指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型

4.set和map数据结构

1.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

// 去除数组的重复成员

[...new Set(array)]

// 去除字符串中的重复字符

[...new Set('ababbc')].join('')

// "abc"

1.set实例属性

Set.prototype.constructor:构造函数,默认就是Set函数。

Set.prototype.size:返回Set实例的成员总数。

2.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():使用回调函数遍历每个成员

由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

2.weakSet

WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。

首先,WeakSet 的成员只能是对象,而不能是其他类型的值。

WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

const a = [[1, 2], [3, 4]];

const ws = new WeakSet(a);

// WeakSet {[1, 2], [3, 4]}

WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。

WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。

WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。

3.map

类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键

get读取:读取未知的键 返回undefined

const set = new Set([

  ['foo', 1],

  ['bar', 2]

]);

const m1 = new Map(set);

m1.get('foo') // 1

const m2 = new Map([['baz', 3]]);

const m3 = new Map(m2);

m3.get('baz') // 3

set存储:对同一键进行两次存储,后面一次会覆盖前面

const map = new Map();

map

.set(1, 'aaa')

.set(1, 'bbb');

map.get(1) // "bbb"

1.实例的属性和操作方法

(1)size属性返回 Map 结构的成员总数

(2)Map.prototype.set(key,value)

set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。

(3)Map.prototype.get(key)

get方法读取key对应的键值,如果找不到key,返回undefined

(4)Map.prototype.has(key)

has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中

(5)Map.prototype.delete(key)

  delete方法删除某个键,返回true。如果删除失败,返回false。

(6)Map.prototype.clear(key)

  clear方法清除所有成员,没有返回值。 

(7)遍历方法

Map.prototype.keys():返回键名的遍历器。

Map.prototype.values():返回键值的遍历器。

Map.prototype.entries():返回所有成员的遍历器。

Map.prototype.forEach():遍历 Map 的所有成员。

const map = new Map([

  [1, 'one'],

  [2, 'two'],

  [3, 'three'],

]);

[...map.keys()]

// [1, 2, 3]

[...map.values()]

// ['one', 'two', 'three']

[...map.entries()]

// [[1,'one'], [2, 'two'], [3, 'three']]

[...map]

// [[1,'one'], [2, 'two'], [3, 'three']]

4.weakMap

WeakMap结构与Map结构类似,也是用于生成键值对的集合 。 WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。

WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名。二是无法清空,即不支持clear方法。因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。

用法: 典型场合就是 DOM 节点作为键名

let myWeakmap = new WeakMap();

myWeakmap.set(

  document.getElementById('logo'),

  {timesClicked: 0})

;

document.getElementById('logo').addEventListener('click', function() {

  let logoData = myWeakmap.get(document.getElementById('logo'));

  logoData.timesClicked++;

}, false);

5.Proxy

在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”。

var proxy = new Proxy(target, handler);

Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

例子:

var proxy = new Proxy({}, {

  get: function(target, propKey) {

    return 35;

  }

});

proxy.time // 35

proxy.name // 35

proxy.title // 35

1.实例方法

1.get()

get方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。

属性链式操作:

var pipe = function (value) {

  var funcStack = [];

  var oproxy = new Proxy({} , {

    get : function (pipeObject, fnName) {

      if (fnName === 'get') {

        return funcStack.reduce(function (val, fn) {

          return fn(val);

        },value);

      }

      funcStack.push(window[fnName]);

      return oproxy;

    }

  });

  return oproxy;

}

var double = n => n * 2;

var pow    = n => n * n;

var reverseInt = n => n.toString().split("").reverse().join("") | 0;

pipe(3).double.pow.reverseInt.get; // 63

2.set()

set方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。

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 // 报错

3.apply()

apply方法拦截函数的调用、call和apply操作。apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

var target = function () { return 'I am the target'; };

var handler = {

  apply: function () {

    return 'I am the proxy';

  }

};

var p = new Proxy(target, handler);

p()

// "I am the proxy"

4.has()

has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。has方法可以接受两个参数,分别是目标对象、需查询的属性名。

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

has()对于for ... in 操作不生效

5.construct()

用于拦截new命令,参数:

target:目标对象

args:构造函数的参数对象

newTarget:创造实例对象时,new命令作用的构造函数(下面例子的p)

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

6.deleteProperty()

用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

7.defineProperty()

拦截了Object.defineProperty()操作

8.getOwnPropertyDescriptor()

getOwnPropertyDescriptor()方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined。

9.getPropertypeof()

getPrototypeOf()方法主要用来拦截获取对象原型。具体来说,拦截下面这些操作。

Object.prototype.__proto__

Object.prototype.isPrototypeOf()

Object.getPrototypeOf()

Reflect.getPrototypeOf()

instanceof

10.isExtensible()

isExtensible()方法拦截Object.isExtensible()操作

11.ownKeys()

ownKeys()方法用来拦截对象自身属性的读取操作

Object.getOwnPropertyNames()

Object.getOwnPropertySymbols()

Object.keys()

for...in循环

12.preventExtensins()

preventExtensions()方法拦截Object.preventExtensions()。该方法必须返回一个布尔值,否则会被自动转为布尔值。

13.setPrototypeof()

setPrototypeOf()方法主要用来拦截Object.setPrototypeOf()方法。

2.实例:web服务的客户端

const service = createWebService('http://example.com/data');

service.employees().then(json => {

  const employees = JSON.parse(json);

  // ···

});

6.Reflect

Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API

目的:

(1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法。

(2) 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。

(3) 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。

(4)Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。

var loggedObj = new Proxy(obj, {

  get(target, name) {

    console.log('get', target, name);

    return Reflect.get(target, name);

  },

  deleteProperty(target, name) {

    console.log('delete' + name);

    return Reflect.deleteProperty(target, name);

  },

  has(target, name) {

    console.log('has' + name);

    return Reflect.has(target, name);

  }

});

1.静态方法

Reflect.apply(target, thisArg, args)

Reflect.apply方法等同于Function.prototype.apply.call(func, thisArg, args),用于绑定this对象后执行给定函数。

Reflect.construct(target, args)

Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法

Reflect.get(target, name, receiver)

Reflect.get方法查找并返回target对象的name属性,如果没有该属性,则返回undefined。

Reflect.set(target, name, value, receiver)

Reflect.set方法设置target对象的name属性等于value。

Reflect.defineProperty(target, name, desc)

Reflect.defineProperty方法基本等同于Object.defineProperty,用来为对象定义属性

Reflect.deleteProperty(target, name)

Reflect.deleteProperty方法等同于delete obj[name],用于删除对象的属性

Reflect.has(target, name)

Reflect.has方法对应name in obj里面的in运算符

Reflect.ownKeys(target)

Reflect.ownKeys方法用于返回对象的所有属性,基本等同于Object.getOwnPropertyNames与Object.getOwnPropertySymbols之和

Reflect.isExtensible(target)

Reflect.isExtensible方法对应Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。

Reflect.preventExtensions(target)

Reflect.preventExtensions对应Object.preventExtensions方法,用于让一个对象变为不可扩展。它返回一个布尔值,表示是否操作成功。

Reflect.getOwnPropertyDescriptor(target, name)

Reflect.getOwnPropertyDescriptor基本等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象

Reflect.getPrototypeOf(target)

Reflect.getPrototypeOf方法用于读取对象的__proto__属性,对应Object.getPrototypeOf(obj)。

Reflect.setPrototypeOf(target, prototype)

Reflect.setPrototypeOf方法用于设置目标对象的原型(prototype),对应Object.setPrototypeOf(obj, newProto)方法。它返回一个布尔值,表示是否设置成功。

2.实例:使用Proxy观察者模式

观察者模式(Observer mode)指的是函数自动观察数据对象,一旦对象有变化,函数就会自动执行

const person = observable({

  name: '张三',

  age: 20

});

function print() {

  console.log(`${person.name}, ${person.age}`)

}

observe(print);

person.name = '李四';

// 输出

// 李四, 20

上面代码中,数据对象person是观察目标,函数print是观察者。一旦数据对象发生变化,print就会自动执行

7.promise

Promise 是异步编程的一种解决方案

特点:

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)

const promise = new Promise(function(resolve, reject) {

  // ... some code

  if (/* 异步操作成功 */){

    resolve(value);

  } else {

    reject(error);

  }

});

1.then

getJSON("/posts.json").then(function(json) {

  return json.post;

}).then(function(post) {

  // ...

});

2.catch

p.then((val) => console.log('fulfilled:', val))

  .catch((err) => console.log('rejected', err));

// 等同于

p.then((val) => console.log('fulfilled:', val))

  .then(null, (err) => console.log("rejected:", err));

3.finally

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作

promise

.then(result => {···})

.catch(error => {···})

.finally(() => {···});

4.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的回调函数。

5.Promise.race()

Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例

const p = Promise.race([p1, p2, p3]);

上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。

6.Promise.allsettled()

Promise.allSettled()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束

const promises = [

  fetch('/api-1'),

  fetch('/api-2'),

  fetch('/api-3'),

];

await Promise.allSettled(promises);

removeLoadingIndicator();

7.Promise.any()

Promise.any()方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例。只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成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);

}

8.Promise.resolve()

将现有对象转化成Promise对象

const jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代码将 jQuery 生成的deferred对象,转为一个新的 Promise 对象。

参数四种情况:

1.参数是一个Promise对象,则不做任何操作直接返回这个实例。

2.参数是一个thenable对象,这转化成Promise对象然后直接执行thenable对象的then方法

let thenable = {

  then: function(resolve, reject) {

    resolve(42);

  }

};

let p1 = Promise.resolve(thenable);

p1.then(function(value) {

  console.log(value);  // 42

});

3.参数是一个不具有then方法的对象,或者不是一个对象,则返回一个新的Promise对象并且状态未resolved

const p = Promise.resolve('Hello');

p.then(function (s){

  console.log(s)

});

// Hello

4.不带参数 ,则直接返回一个状态为resolved的Promise对象

需要注意的是,立即resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的结束时执行,而不是在下一轮“事件循环”的开始时。

9.Promise.reject()

返回一个新的 Promise 实例,该实例的状态为rejected。

10.Promise.try()

让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 

const f = () => console.log('now');

Promise.try(f);

console.log('next');

// now

// next

等同于:

const f = () => console.log('now');

(async () => f())();

console.log('next');

// now

// next

但是  async () => f()会吃掉f()抛出的错误。 所有需要使用promise.catch

(async () => f())()

.then(...)

.catch(...)

8.Iterator和for .. of循环

1.Iterator

它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。

作用:

1.各种数据结构,提供一个统一的、简便的访问接口

2.使得数据结构的成员能够按某种次序排列

3.ES6 创造了一种新的遍历命令for...of循环,Iterator 接口主要供for...of消费。

遍历过程:

(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。

(2)第一次调用指针对象的next方法,可以将指针指向数据结构的第一个成员。

(3)第二次调用指针对象的next方法,指针就指向数据结构的第二个成员。

(4)不断调用指针对象的next方法,直到它指向数据结构的结束位置。

var it = makeIterator(['a', 'b']);

it.next() // { value: "a", done: false }

it.next() // { value: "b", done: false }

it.next() // { value: undefined, done: true }

function makeIterator(array) {

  var nextIndex = 0;

  return {

    next: function() {

      return nextIndex < array.length ?

        {value: array[nextIndex++], done: false} :

        {value: undefined, done: true};

    }

  };

}

调用iterator的场合:

1.解构赋值

对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法

let set = new Set().add('a').add('b').add('c');

let [x,y] = set;

// x='a'; y='b'

let [first, ...rest] = set;

// first='a'; rest=['b','c'];

2.扩展运算符

// 例一

var str = 'hello';

[...str] //  ['h','e','l','l','o']

// 例二

let arr = ['b', 'c'];

['a', ...arr, 'd']

// ['a', 'b', 'c', 'd']

3.yield*

yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。

let generator = function* () {

  yield 1;

  yield* [2,3,4];

  yield 5;

};

var iterator = generator();

iterator.next() // { value: 1, done: false }

iterator.next() // { value: 2, done: false }

iterator.next() // { value: 3, done: false }

iterator.next() // { value: 4, done: false }

iterator.next() // { value: 5, done: false }

iterator.next() // { value: undefined, done: true }

4.其他场合

for...of

Array.from()

Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))

Promise.all()

Promise.race()

9.Generator

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

形式上,Generator 函数是一个普通函数,

但是有两个特征。

一是,function关键字与函数名之间有一个星号;

二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

function* helloWorldGenerator() {

  yield 'hello';

  yield 'world';

  return 'ending';

}

var hw = helloWorldGenerator();

hw.next()

// { value: 'hello', done: false }

hw.next()

// { value: 'world', done: false }

hw.next()

// { value: 'ending', done: true }

hw.next()

// { value: undefined, done: true }

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。

下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。

1.yield

yield表达式就是暂停标志。 只能在Generator函数中使用。

暂缓执行函数 :

function* f() {

yield console.log('执行了!')

}

var generator = f();

setTimeout(function () {

  generator.next()

}, 2000);

另外,yield表达式如果用在另一个表达式之中,必须放在圆括号里面。

function* demo() {

  console.log('Hello' + yield); // SyntaxError

  console.log('Hello' + yield 123); // SyntaxError

  console.log('Hello' + (yield)); // OK

  console.log('Hello' + (yield 123)); // OK

}

2.与iterator的关系

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

var myIterable = {};

myIterable[Symbol.iterator] = function* () {

  yield 1;

  yield 2;

  yield 3;

};

[...myIterable] // [1, 2, 3]

Generator 函数赋值给Symbol.iterator属性,从而使得myIterable对象具有了 Iterator 接口,可以被...运算符遍历了

3.next方法的参数

yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

function* f() {

  for(var i = 0; true; i++) {

    var reset = yield i;

    if(reset) { i = -1; }

  }

}

var g = f();

g.next() // { value: 0, done: false }

g.next() // { value: 1, done: false }

g.next(true) // { value: 0, done: false }

function* foo(x) {

  var y = 2 * (yield (x + 1));

  var z = yield (y / 3);

  return (x + y + z);

}

var a = foo(5);

a.next() // Object{value:6, done:false}

a.next() // Object{value:NaN, done:false}

a.next() // Object{value:NaN, done:true}

var b = foo(5);

b.next() // { value:6, done:false }

b.next(12) // { value:8, done:false }

b.next(13) // { value:42, done:true }

上面代码中,第二次运行next方法的时候不带参数,导致 y 的值等于2 * undefined(即NaN),除以 3 以后还是NaN,因此返回对象的value属性也等于NaN。第三次运行Next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN。

如果向next方法提供参数,返回结果就完全不一样了。上面代码第一次调用b的next方法时,返回x+1的值6;第二次调用next方法,将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;第三次调用next方法,将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

注意,由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的。V8 引擎直接忽略第一次使用next方法时的参数,只有从第二次使用next方法开始,参数才是有效的。从语义上讲,第一个next方法用来启动遍历器对象,所以不用带有参数

function* dataConsumer() {

  console.log('Started');

  console.log(`1. ${yield}`);

  console.log(`2. ${yield}`);

  return 'result';

}

let genObj = dataConsumer();

genObj.next();

// Started

genObj.next('a')

// 1. a

genObj.next('b')

// 2. b

4.for .... fo

for...of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,且此时不再需要调用next方法。

function* foo() {

  yield 1;

  yield 2;

  yield 3;

  yield 4;

  yield 5;

  return 6;

}

for (let v of foo()) {

  console.log(v);

}

// 1 2 3 4 5

注意:一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。

除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。这意味着,它们都可以将 Generator 函数返回的 Iterator 对象,作为参数。

function* numbers () {

  yield 1

  yield 2

  return 3

  yield 4

}

// 扩展运算符

[...numbers()] // [1, 2]

// Array.from 方法

Array.from(numbers()) // [1, 2]

// 解构赋值

let [x, y] = numbers();

x // 1

y // 2

// for...of 循环

for (let n of numbers()) {

  console.log(n)

}

// 1

// 2

5.Generator.prototype.throw()

可以在函数体外抛出错误,然后在 Generator 函数体内捕获。

var g = function* () {

  try {

    yield;

  } catch (e) {

    console.log('内部捕获', e);

  }

};

var i = g();

i.next();

try {

  i.throw('a');

  i.throw('b');

} catch (e) {

  console.log('外部捕获', e);

}

// 内部捕获 a

// 外部捕获 b

上面代码中,遍历器对象i连续抛出两个错误。第一个错误被 Generator 函数体内的catch语句捕获。i第二次抛出错误,由于 Generator 函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的catch语句捕获。

throw可以接受一个参数,该参数会被catch语句接收,建议抛出Error对象的实例。

var g = function* () {

  try {

    yield;

  } catch (e) {

    console.log(e);

  }

};

var i = g();

i.next();

i.throw(new Error('出错了!'));

// Error: 出错了!(…)

6.Generator.prototype.return

可以返回给定的值,并且终结遍历 Generator 函数。

function* gen() {

  yield 1;

  yield 2;

  yield 3;

}

var g = gen();

g.next()        // { value: 1, done: false }

g.return('foo') // { value: "foo", done: true }

g.next()        // { value: undefined, done: true }

7.yield*

从语法角度看,如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式。

function* bar() {

  yield 'x';

  yield* foo();

  yield 'y';

}

// 等同于

function* bar() {

  yield 'x';

  yield 'a';

  yield 'b';

  yield 'y';

}

// 等同于

function* bar() {

  yield 'x';

  for (let v of foo()) {

    yield v;

  }

  yield 'y';

}

for (let v of bar()){

  console.log(v);

}

// "x"

// "a"

// "b"

// "y"

8.作为对象属性的Denerator函数

let obj = {

  * myGeneratorMethod() {

    ···

  }

};

9.Generator与状态机

每次运行改变状态:

var ticking = true;

var clock = function() {

  if (ticking)

    console.log('Tick!');

  else

    console.log('Tock!');

  ticking = !ticking;

}

等同于:

var clock = function* () {

  while (true) {

    console.log('Tick!');

    yield;

    console.log('Tock!');

    yield;

  }

};

10.应用

1.异步操作的同步话表达

处理异步操作,改写回调函数

function* loadUI() {

  showLoadingScreen();

  yield loadUIDataAsynchronously();

  hideLoadingScreen();

}

var loader = loadUI();

// 加载UI

loader.next()

// 卸载UI

loader.next()

Ajax:

function* main() {

  var result = yield request("http://some.url");

  var resp = JSON.parse(result);

    console.log(resp.value);

}

function request(url) {

  makeAjaxCall(url, function(response){

    it.next(response);

  });

}

var it = main();

it.next();

传统异步编程:

回调函数

事件监听

发布/订阅

Promise 对象

10.async

就是 Generator 函数的语法糖

const fs = require('fs');

const readFile = function (fileName) {

  return new Promise(function (resolve, reject) {

    fs.readFile(fileName, function(error, data) {

      if (error) return reject(error);

      resolve(data);

    });

  });

};

const gen = function* () {

  const f1 = yield readFile('/etc/fstab');

  const f2 = yield readFile('/etc/shells');

  console.log(f1.toString());

  console.log(f2.toString());

};

换成async:

const asyncReadFile = async function () {

  const f1 = await readFile('/etc/fstab');

  const f2 = await readFile('/etc/shells');

  console.log(f1.toString());

  console.log(f2.toString());

};

async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await

改进点:

1.内置执行器 ,async函数的执行,与普通函数一模一样,只要一行 asyncReadFile();

2.更好的语义, async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

3.更广的适用性 co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)

4.返回值时promise

1.用法

function timeout(ms) {

  return new Promise((resolve) => {

    setTimeout(resolve, ms);

  });

}

async function asyncPrint(value, ms) {

  await timeout(ms);

  console.log(value);

}

asyncPrint('hello world', 50);

async函数返回的 Promise 对象,必须等到内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数

例子:

依次远程读取一组 URL,然后按照读取的顺序输出结果。

async function logInOrder(urls) {

  // 并发读取远程URL

  const textPromises = urls.map(async url => {

    const response = await fetch(url);

    return response.text();

  });

  // 按次序输出

  for (const textPromise of textPromises) {

    console.log(await textPromise);

  }

}

2.使用注意

1.await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

async function myFunction() {

  try {

    await somethingThatReturnsAPromise();

  } catch (err) {

    console.log(err);

  }

}

// 另一种写法

async function myFunction() {

  await somethingThatReturnsAPromise()

  .catch(function (err) {

    console.log(err);

  });

}

2.多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

let foo = await getFoo();

let bar = await getBar();

3.await命令只能用在async函数之中,如果用在普通函数,就会报错

4.async 函数可以保留运行堆栈。

const a = () => {

  b().then(() => c());

};

函数a内部运行了一个异步任务b()。当b()运行的时候,函数a()不会中断,而是继续执行。等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。如果b()或c()报错,错误堆栈将不包括a()。

改变:

const a = async () => {

  await b();

  c();

};

11.Class的基本语法

1.constructor方法

constructor方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

实例的属性除非显式定义在其本身(即定义在this对象上),否则都是定义在原型上(即定义在class上)。

//定义类

class Point {

  constructor(x, y) {

    this.x = x;

    this.y = y;

  }

  toString() {

    return '(' + this.x + ', ' + this.y + ')';

  }

}

var point = new Point(2, 3);

point.toString() // (2, 3)

point.hasOwnProperty('x') // true

point.hasOwnProperty('y') // true

point.hasOwnProperty('toString') // false

point.__proto__.hasOwnProperty('toString') // true

2.取值getter和存值setter

class MyClass {

  constructor() {

    // ...

  }

  get prop() {

    return 'getter';

  }

  set prop(value) {

    console.log('setter: '+value);

  }

}

let inst = new MyClass();

inst.prop = 123;

// setter: 123

inst.prop

// 'getter

3.注意点

1.严格模式,类和模块的内容默认使用严格模式,不需要再次 用use strict

2.不存在提升,即在定义之前使用就会报错

3.name属性

4.Generator方法,如果在某个方法之前加上  *  号,则表示该方法是一个Generator方法

class Foo {

  constructor(...args) {

    this.args = args;

  }

  * [Symbol.iterator]() {

    for (let arg of this.args) {

      yield arg;

    }

  }

}

for (let x of new Foo('hello', 'world')) {

  console.log(x);

}

// hello

// world

5.this的指向,在类的方法内部如果包含this,则默认指向类的实例

4.静态方法

1.static

如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

class Foo {

  static classMethod() {

    return 'hello';

  }

}

Foo.classMethod() // 'hello'

var foo = new Foo();

foo.classMethod()

// TypeError: foo.classMethod is not a function

如果静态方法包含this关键字,这个this指的是类,而不是实例。

5.静态属性

静态属性指的是 Class 本身的属性,即Class.propName,而不是定义在实例对象(this)上的属性。

// 老写法

class Foo {

  // ...

}

Foo.prop = 1;

// 新写法

class Foo {

  static prop = 1;

}

12.Class的继承

1.extends

在子类的构造函数中,只有调用super之后,才可以使用this关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有super方法才能调用父类实例。

class Point {

  constructor(x, y) {

    this.x = x;

    this.y = y;

  }

}

class ColorPoint extends Point {

  constructor(x, y, color) {

    this.color = color; // ReferenceError

    super(x, y);

    this.color = color; // 正确

  }

}

Object.getPrototypeOf()判断一个类是否继承某一个类

2.super

第一种情况,super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。

class A {}

class B extends A {

  m() {

    super(); // 报错

  }

}

第二种情况,super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

class A {

  p() {

    return 2;

  }

}

class B extends A {

  constructor() {

    super();

    console.log(super.p()); // 2

  }

}

let b = new B();

3.mixin模式

Mixin 指的是多个对象合成一个新的对象,新对象具有各个组成成员的接口

13.Module

1.es6模块中默认使用严格模式

严格模式限制:

变量必须声明后再使用

函数的参数不能有同名属性,否则报错

不能使用with语句

不能对只读属性赋值,否则报错

不能使用前缀 0 表示八进制数,否则报错

不能删除不可删除的属性,否则报错

不能删除变量delete prop,会报错,只能删除属性delete global[prop]

eval不会在它的外层作用域引入变量

eval和arguments不能被重新赋值

arguments不会自动反映函数参数的变化

不能使用arguments.callee

不能使用arguments.caller

禁止this指向全局对象

不能使用fn.caller和fn.arguments获取函数调用的堆栈

增加了保留字(比如protected、static和interface)

2.export命令

模块功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

// profile.js

var firstName = 'Michael';

var lastName = 'Jackson';

var year = 1958;

export { firstName, lastName, year };

// 报错

function f() {}

export f;

// 正确

export function f() {};

// 正确

function f() {}

export {f};

3.import命令

// main.js

import { firstName, lastName, year } from './profile.js';

function setName(element) {

  element.textContent = firstName + ' ' + lastName;

}

4.模块的整体加载

除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

// circle.js

export function area(radius) {

  return Math.PI * radius * radius;

}

export function circumference(radius) {

  return 2 * Math.PI * radius;

}

import * as circle from './circle';

console.log('圆面积:' + circle.area(4));

console.log('圆周长:' + circle.circumference(14));

5.export default命令

// 第一组

export default function crc32() { // 输出

  // ...

}

import crc32 from 'crc32'; // 输入

// 第二组

export function crc32() { // 输出

  // ...

};

import {crc32} from 'crc32'; // 输入

6.export和import复合使用

如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起

export { foo, bar } from 'my_module';

// 可以简单理解为

import { foo, bar } from 'my_module';

export { foo, bar };

引入+改名+导出

export * as ns from "mod";

// 等同于

import * as ns from "mod";

export {ns};

7.跨模块常量

// constants.js 模块

export const A = 1;

export const B = 3;

export const C = 4;

// test1.js 模块

import * as constants from './constants';

console.log(constants.A); // 1

console.log(constants.B); // 3

// test2.js 模块

import {A, B} from './constants';

console.log(A); // 1

console.log(B); // 3

8.ES6模块和CommonJS模块的差异

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。

CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

14.修饰符

装饰器(Decorator)是一种与类(class)相关的语法,用来注释或修改类和类方法。  装饰器是一个对类进行处理的函数。装饰器函数的第一个参数,就是所要装饰的目标类。

@testable

class MyTestableClass {

  // ...

}

function testable(target) {

  target.isTestable = true;

}

MyTestableClass.isTestable // true

上面代码中,@testable就是一个装饰器。它修改了MyTestableClass这个类的行为,为它加上了静态属性isTestable。testable函数的参数target是MyTestableClass类本身。

装饰器能在编译阶段运行代码。也就是说,装饰器本质就是编译时执行的函数。

如果同一个方法有多个装饰器,会像剥洋葱一样,先从外到内进入,然后由内向外执行。

装饰器只能用于类和类的方法,不能用于函数,因为存在函数提升。

15.卸载监听器时的陷阱

错误做法:

class PauseMenu extends React.Component{

    componentWillMount(){

        AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));

    }

    componentWillUnmount(){

        AppStateIOS.removeEventListener('change', this.onAppPaused.bind(this));

    }

    onAppPaused(event){

    }

}

正确做法:

class PauseMenu extends React.Component{

    constructor(props){

        super(props);

        this._onAppPaused = this.onAppPaused.bind(this);

    }

    componentWillMount(){

        AppStateIOS.addEventListener('change', this._onAppPaused);

    }

    componentWillUnmount(){

        AppStateIOS.removeEventListener('change', this._onAppPaused);

    }

    onAppPaused(event){

    }

}

16.正则表达式命名捕获组

ES2018允许命名捕获组使用符号?,在打开捕获括号

const reDate = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/,

  match  = reDate.exec('2018-04-30'),

  year  = match.groups.year,  // 2018

  month  = match.groups.month, // 04

  day    = match.groups.day;  // 30

任何匹配失败的命名组都将返回undefined。

正则表达式反向断言

肯定反向断言:

目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号:

const

  reLookahead = /\D(?=\d+)/,

  match      = reLookahead.exec('$123.89');

console.log( match[0] ); // $

ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字

const

  reLookbehind = /(?<=\D)\d+/,

  match        = reLookbehind.exec('$123.89');

console.log( match[0] ); // 123.89

否定反向断言:

表示一个值必须不存在,

const

  reLookbehindNeg = /(?<!\D)\d+/,

  match          = reLookbehind.exec('$123.89');

console.log( match[0] ); // null

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350