ES6必知必会知识

来源:互联网 发布:床单跟床笠哪个好 知乎 编辑:程序博客网 时间:2024/06/06 08:23

    • let 和 const
    • 变量的解构赋值
    • 字符串的拓展
    • 函数的拓展
    • 数组的扩展
    • 对象的拓展
    • Symbol
    • Set 和 Map
    • Promise 对象

let 和 const

  1. let声明的变量只有在当前代码块成效,而且不具备变量提升(即代码块中有let声明的变量提前使用会报错);
{   console.log(a)    // a is not defined   console.log(b)   //  2   let a = 1;   var b = 2;}
  1. let同一作用域内不允许重复声明;
  2. const声明一个只读常量,一旦声明,无法更改;
  3. const声明一个变量的时候,必须初始化,而且该变量只能在当前作用域有效;
  4. const声明一个符合类型的数据时(主要是对象和数组),保存的是变量的内存地址,只能保证这个地址固定,不能保证数据结构不变;
const o = {};o.name = 'hello';    //可以给对象添加属性console.log(o);      // { "name" : "hello" }o = {};             //报错,因为o的内存地址不能改变
  1. 可以使用 Object.freeze()方法来冻结一个对象或者对象的某个属性;
  2. ES5声明变量的方式有两种 : var 和 function; ES6声明变量的方式有六种 : var let const function import class;

变量的解构赋值

  1. ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值;
  2. 如果变量解构不成功就会返回 undefined;
  3. 只要某种数据结构具有 Iterator 接口,都可以采用数组形式的解构赋值;
  4. 解构赋值时可以指定默认值 ( let [ foo = true ] = [] );
  5. 如果数组的某个成员不严格等于( === ) undefined , 默认值就不会生效;
let [x, y = 'b'] = ['a']; // x='a', y='b'let [x, y = 'b'] = ['a', 'undefined'];  // x = 'a' , y = 'undefined'
  1. 如果指定的默认值是一个表达式,那么该表达式是惰性求值(即只有在使用到的时候才去求值);

  2. 默认值可以引用解构赋值的其他变量,但该变量必须已经声明;

  3. 对象的解构中,变量必须与属性同名,才能取到正确的值,如果变量没有对应的同名属性,则会导致取不到值,最后等于undefined;

  4. 对象的解构赋值是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者;

let { foo: baz , bar } = { foo: "aaa", bar: "bbb" }; foo // foo is not definedbaz // 'aaa'bar // 'bbb'

上述例子中 foo 是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo , bar之所以能匹配,是因为对象的解构赋值是下面形式的简写

let { foo: foo, bar: bar } = { foo: "aaa", bar: "bbb" }
  1. 变量的解构赋值通常用于交换变量,函数参数定义,参数指定默认值等,虽然解构赋值虽然很方便,但是解析起来并不容易,如果使用不当就会产生与预期值不同的问题~

字符串的拓展

  1. ES6为字符串添加了遍历器接口,因此可以使用for…of循环遍历字符串
  2. 字符串新增的 includes()、startsWith()、endsWidth() 三个方法用于判断某一字符串是否包含于另一字符串

    • includes():返回布尔值,表示源字符串中是否包含参数字符串。
    • startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
    • endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。
  3. 新增 repeat() 方法, 该方法返回一个新字符串,不是对原字符串操作,表示将原字符串重复n次。

let str = 'abc';str.repeat(3) //abcabcabc str //abc

ps:该方法参数为正整数,如果为负数会报错,小数向下取整;

  1. 新增 padStart(),padEnd() 方法,用于补全字符串,该方法返回一个新字符串,不是对原字符串操作,传入两个参数,第一个参数用来指定字符串的最小长度,第二个参数是用来补全的字符串(缺省的话默认空格补全)。(ps:如果原字符串的长度,等于或大于指定的最小长度,则返回原字符串);
let str = 'abc';str.padStart(2, 'abc') // 'abc'str.padEnd(2, 'abc') // 'abc'
  1. 模板字符串··(esc下面的那个按键),可以摆脱传统的字符串拼接的模式,直接将变量(表达式,对象的引用,函数等)写在模板字符串中输出
let str = 'world';let html = `hello ${str}`;html //hello wrold

ps:所有模板字符串的空格和换行,都是被保留的,可以使用trim方法消除换行。

  1. 标签模板,即模板字符串紧跟在一个函数名后面,该函数将被调用来处理这个模板字符串,这中方式被称为“标签模板”,“标签”指的就是函数,紧跟在后面的模板字符串就是它的参数
console.log `123`// 等同于console.log (123)
  1. 如果模板字符里面有变量,会将模板字符串先处理成多个参数,再调用函数;
var a = 5;var b = 10;function tag(s, v1, v2) {  console.log(s)  console.log(v1);  console.log(v2);}tag`Hello ${ a + b } world ${ a * b }`;//['Hello','world','']//15//50

可以看出,tag函数第一个参数是一个数组,数组的成员是模板字符串中那些没有变量替换的部分,其他参数,都是模板字符串各个变量被替换后的值;

  1. 再来一个例子,看看标签模板的使用方法:
var total = 30;var msg = passthru`The total is ${total} (${total*1.05} with tax)`;function passthru(literals) {  var result = '';  var i = 0;  while (i < literals.length) {    result += literals[i++];    if (i < arguments.length) {      result += arguments[i];    }  }  return result;}

上述例子中,参数 literals 实际上是 ["The total is "," ("," with tax)"], 函数内部 arguments 的值是 { "0" : ["The total is "," ("," with tax)"] , "1" : 30 , "2" : 31.5 },通过以上梳理,可以将各个参数按照原来的位置拼合回去,最终得到输出为”The total is 30 (31.5 with tax)”

ps:在使用标签模板的时候,要注意参数的位置,可根据自己实际的需求进行修改,返回正确的结果;

函数的拓展

  1. ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面,一目了然,十分实用
function say( x , y = 'World') {    console.log( x , y);}say('Hello')  // Hello Worldsay('Hello','Kite')  //Hello Kite
  1. 函数参数默认已经声明,不能再用 let 或者 const 声明,而且不能有同名参数

  2. 一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失;

var x = 1;function f(x, y = x) {  console.log(y);}f(2) // 2

上面例子中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2;

let x = 1;function f( y = x ) {  let x = 2;  console.log(y);}f() // 1

上面例子中,函数f调用时,参数y=x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x;

  1. 函数声明时,可以将某个参数默认值设为undefined,表明这个参数是可以省略的;

  2. rest 参数(形式为…变量名),用于获取函数的多余参数,该变量是一个数组,用于将多余的参数放入该数组中。(rest 参数之后不能再有其他参数,它只能是函数的最后一个参数,否则会报错)

function func(...params){    console.log(params)}func(1,2,3) // [1,2,3]function func( x , ...params){    console.log(params)}func(1,2,3) // [2,3]
  1. 箭头函数(=>),ES6 允许使用“箭头”(=>)定义函数,这种写法相比较 ES5 定义 function 看起来简洁得多;
var func = x => x //等同于var func = function func(x) {    return x;};
  1. 如果箭头函数没有参数或者有多个参数的话,则需要加上()来进行声明;
var func = () => 'Hello World';//等同于var func = function func() {  return 'Hello World';};var func = ( x , y ) => x + y//等同于var func = function func(x, y) {  return x + y;};
  1. 如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回;
var func = ( x , y ) => { return x + y; }
  1. 如果箭头函数直接返回一个对象,必须在对象外面加上括号;
var func = ( x , y ) => ({ x : x , y : y })
  1. 箭头函数使用时必须注意以下几个问题:

函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象;

function foo() {  setTimeout(() => {    console.log('id:', this.id);  }, 100);}var id = 21;foo.call({ id: 42 });  //42

上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到100毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。

箭头函数不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误;

不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替

不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

数组的扩展

  1. 拓展运算符(’…’),它相当于rest参数的逆运算,用于将一个数组转换为用逗号分隔的参数序列;
console.log(...[1, 2, 3])// 1 2 3console.log(1, ...[2, 3, 4], 5)// 1 2 3 4 5
  1. 如果扩展运算符后面是一个空数组,则不产生任何效果;
[...[], 1]// [1]
  1. 常见的拓展运算符的应用:
//合并数组let arr1 = [1,2];let arr2 = [3,4];let arr3 = [5,6];let newArr = [...arr1 , ...arr2 , ...arr3];  //等同于ES5 [].concat( arr1 , arr2 , arr3 )// [1,2,3,4,5,6]//与解构赋值结合(用于生成数组)const [ val , ...rest] = [1, 2, 3, 4, 5];val // 1rest  // [2, 3, 4, 5]//将字符串转为真正的数组let str = 'mine';[...str]  // ["m","i","n","e"]//可以将类数组转化成正真的数组 let arrayLike = {    0 : 'div.class1' ,    1 : 'div.class2' ,    2 : 'div.class3' ,    length : 3}console.log([...arrayLike])  // ["div.class1","div.class2","div.class3"]
  1. 新增 Array.from 方法,可以将类似数组的对象(array-like object)和可遍历(iterable)的对象转化成真正的数组;该方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组;
let arr = [ 1 , 2 , 3];arr.map( x => x * x);// [ 1 , 4 , 9 ]Array.from(arr, (x) => x * x)// [ 1 , 4 , 9 ]
  1. 新增 Array.of 方法 ,用于将一组值,转换为数组(该方法基本上可以用来替代Array()或new Array(),避免出现参数不同而导致的重载);
//传统ArrayArray() // []Array(3) // [, , ,]Array(1, 2, 3) // [1, 2, 3]//Array.ofArray.of() // []Array.of(undefined) // [undefined]Array.of(1) // [1]Array.of(1, 2) // [1, 2]
  1. 数组实例方法 find() 用于找出第一个符合条件的数组成员,该方法的参数是一个回调函数,该回调函数可以接收三个参数,依次是当前元素,当前元素索引,数组本身;如果查找成功,返回符合条件的第一个成员,如果没有符合条件的成员,则返回undefined;
var arr = [1, 2, 4, 5];var r = arr.find(function( element , index , self ){    return element % 2 == 0;})r  // 2
  1. 数组实例方法 findIndex() , 该方法的参数与 find() 一样 , 不同的是该方法 返回的是第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1;
var arr = [1, 2, 4, 5];var r = arr.find(function( element , index , self ){    return element % 2 == 0;})r  // 1

ps:find() 和 findIndex() 这两个方法都可以发现NaN,弥补了数组的IndexOf方法的不足。

  1. 数组实例方法 includes() , 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似;该方法接收两个参数,第一个参数是要查找的成员,第二个参数表示搜索的起始位置(如果为负数,则表示倒数的位置,如果大于数组长度,则会重置为从0开始)
[1, 2, 3].includes(2)     // true[1, 2, 3].includes(4)     // false[1, 2, 3].includes(3, 3);  // false[1, 2, 3].includes(3, -1); // true

对象的拓展

  1. ES6 允许直接写入变量和函数,作为对象的属性和方法(在对象中,直接写变量时,属性名为变量名, 属性值为变量的值)
//属性简写var foo = 'bar';var obj = {foo};obj // { foo : "bar" }//变量简写var mine = {    foo ,     method(){        //to do    }}
  1. ES6 允许字面量定义对象时,用表达式作为对象的属性名或者方法名,即把表达式放在方括号内;
let propKey = 'foo';let obj = {  [propKey]: true,  ['a' + 'bc']: 123,  ['s' + 'ay'](){    console.log('hello world')  }}obj // {"foo":true,"abc":123}obj.say()  // 'hello world'
  1. 新增 Object.is() 方法,用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致,不同之处在于一是+0不等于-0,二是NaN等于自身。
+0 === -0 //trueNaN === NaN // falseObject.is(+0, -0) // falseObject.is(NaN, NaN) // true
  1. 新增 Object.assign 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),第一个参数是目标对象,后面的参数都是源对象;
var target = { a: 1 };var source1 = { b: 2 };var source2 = { c: 3 };Object.assign(target, source1, source2);target // {a:1, b:2, c:3}

ps:如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

var target = { a: 1, b: 1 };var source1 = { b: 2, c: 2 };var source2 = { c: 3 };Object.assign(target, source1, source2);target // {a:1, b:2, c:3}

该方法不能用于目标对象是 undefined 和 null 上, 会报错;

  1. Object.assign 方法实行的是浅拷贝,而不是深拷贝。如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用,修改会对原对象造成影响;
var obj1 = {a: {b: 1}};var obj2 = Object.assign({}, obj1);obj1.a.b = 2;obj2.a.b     // 2
  1. Object.assign 方法常用于以下几个方面

为对象添加属性

var _this = {};Object.assign( _this , { name : 'mine' } );_this // { name : 'mine' }

为对象添加方法

var _this = {};Object.assign( _this , { func(){ console.log('hello world') } } );_this.func()  // hello world

克隆对象

var _this = { name : 'mine' };Object.assign( {} , _this );

合并多个对象

var _this = {};var source1 = { name : 'mine' };var source2 = { mail : 'your' };Object.assign( _this , source1 , source2 );_this // {"name":"mine","mail":"your"}

为属性指定默认值

var default = {  name : 'mine' , mail : 'your' }function processContent(options) {    options = Object.assign({}, default , options);    // to do}
  1. Object.setPrototypeOf方法的作用与_proto_相同,用来设置一个对象的prototype对象,返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
  2. 8.
let proto = {};let obj = { x: 10 };Object.setPrototypeOf(obj, proto);proto.y = 20;proto.z = 40;obj.x // 10obj.y // 20obj.z // 40
  1. Object.getPrototypeOf()方法,该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。

  2. Object.keys(),Object.values(),Object.entries() 除第一个外,后面两个是ES6新增的方法,用于遍历对象,返回都是数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键、值和键值对数组。

let obj = { a : 1 , b : 'hello' }Object.keys( obj );     // ["a","b"]Object.values( obj );   // [1,"hello"]Object.entries( obj );  // [["a",1],["b","hello"]]

Symbol

  1. Symbol 是 ES6 引入了一种新的原始数据类型,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种分别是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object);
  2. Symbol 值通过Symbol函数生成,可以作为对象的属性名使用,保证不会与其他属性名产生冲突;
let s = Symbol();typeof s  // symbol

ps:上面代码表示创建一个Symbol变量,值得注意的是,Symbol函数前不能使用new命令,否则会报错,也就是说Symbol 是一个原始类型的值,不是对象,也不能添加属性;

  1. Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要用于区分不同的 Symbol 变量;
let s1 = Symbol('a');let s2 = Symbol('b');s1.toString()  // 'Symbol(a)'s2.toString()  // 'Symbol(b)'

ps:Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的

let s1 = Symbol('a');let s2 = Symbol('a');s1 === s2  //false
  1. Symbol 值不能与其他类型的值进行运算,但可以转为布尔值,但是不能转为数值;
let s = Symbol();s + '2'       // Cannot convert a Symbol value to a stringBoolean(s)    // true!s            // false
  1. 用于对象的属性名,可以保证不会出现同名的属性,对于一个对象由多个模块构成的情况非常有用,能防止某一个键被不小心改写或覆盖;值得注意的是,Symbol 值作为对象属性名时,不能用点运算符,因为点运算符后面是一个字符串;
let s = Symbol();let obj = {};obj[s] = 'hello world';//或者let obj  = {    [s] : 'hello world'}obj.s   // undefinedobj[s]  // hello world
  1. Symbol 作为属性名,不会被常规方法遍历得到,即该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回,但是,它并不是私有属性,可以使用 Object.getOwnPropertySymbols 方法,可以获取指定对象的所有 Symbol 属性名;
var obj = {};var a = Symbol('a');var b = Symbol('b');obj[a] = 'Hello';obj[b] = 'World';obj.c  = 'Mine';for( let key in obj ){   console.log(key)         // c}var objectSymbols = Object.getOwnPropertySymbols(obj);console.log(objectSymbols) // [Symbol(a), Symbol(b)]
  1. Symbol.for方法接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值;它与Symbol()不同的是,Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值,而 Symbol()每次都会返回3不同的Symbol值;
Symbol.for("name") === Symbol.for("name")// trueSymbol("name") === Symbol("name")// false
  1. Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key,而Symbol()写法是没有登记机制的;
var s1 = Symbol.for("name");Symbol.keyFor(s1) // "name"var s2 = Symbol("name");Symbol.keyFor(s2) // undefined

ps:Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值

Set 和 Map

  1. ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值,它 本身是一个构造函数,用来生成 Set 数据结构。
let s = new Set([1,2,3,4,5,2,2,3,5]);s // [1,2,3,4,5]
  1. 可以使用add(key)方法可以添加元素到Set中,可以重复添加,但不会有效果,值得注意的是向Set加入值的时候,不会发生类型转换,即 5 和 “5” 是两个不同的值,但在 Set 内部,两个NaN是相等
let s = new Set([1,2,3]);s.add(4)   //[1,2,3,4]s.add(4)   //[1,2,3,4]s.add(5)   //[1,2,3,4,5]s.add('5') //[1,2,3,4,5,"5"]s.add(NaN) //[1,2,3,4,5,"5",NaN]s.add(NaN) //[1,2,3,4,5,"5",NaN]
  1. 可以利用Set数据不重复的特性,提供一种新的数组去重方法
// 去除数组的重复成员[...new Set(array)][...new Set([1,2,2,3,3,4,5,5])]  //[1,2,3,4,5]
  1. Set常见的操作方法有:

add(value):添加某个值,返回Set结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为Set的成员。
clear():清除所有成员,没有返回值。

s.add(1).add(2).add(2);// 注意2被加入了两次s.size // 2s.has(1) // trues.has(2) // trues.has(3) // falses.delete(2);s.has(2) // 
  1. Set 结构的实例有四个遍历方法,可以用于遍历成员。

    • keys():返回键名的遍历器
    • values():返回键值的遍历器
    • entries():返回键值对的遍历器
    • forEach():使用回调函数遍历每个成员

需要特别指出的是,Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用Set保存一个回调函数列表,调用时就能保证按照添加顺序调用。

let set = new Set(['red', 'green', 'blue']);for (let item of set.keys()) {  console.log(item);}// red// green// bluefor (let item of set.values()) {  console.log(item);}// red// green// bluefor (let item of set.entries()) {  console.log(item);}// ["red", "red"]// ["green", "green"]// ["blue", "blue"]
  1. ES6 提供了 Map 数据结构,它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适;

  2. Map常见的操作方法有:

    • set(key,val):添加某个值,返回Map结构本身。
    • get(key): 读取某个键,如果该键未知,则返回undefined
    • delete(key): 删除某个键,返回一个布尔值,表示删除是否成功。
    • has(key): 返回一个布尔值,表示该值是否为Map的键。
    • clear() : 清除所有成员,没有返回值。
const m = new Map();const o = { str : 'Hello World'};m.set(o, 'content')m.get(o) // "content"m.has(o) // truem.delete(o) // truem.has(o) // false
  1. 只有对同一个对象的引用,Map 结构才将其视为同一个键
const map = new Map();const k1 = ['a'];const k2 = ['a'];map.set(k1, 111).set(k2, 222);map.get(k1) // 111map.get(k2) // 222

上面例子表明,Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键,因为 k1 和 k2 是两个不同的对象,放在不同的内存地址中,所以Map视为不同的键

  1. Map 结构原生提供三个遍历器生成函数和一个遍历方法。

    • keys():返回键名的遍历器。
    • values():返回键值的遍历器。
    • entries():返回所有成员的遍历器。
    • forEach():遍历 Map 的所有成员。

ps:Map 的遍历顺序就是插入顺序,这里就不示例了,大家自己动手实践一下。

  1. 可以使用扩展运算符(…)将Map转换为数组,反过来,将数组传入 Map 构造函数,就可以转为 Map了
//Map转数组const map = new Map();map.set('name' , 'hello').set({},'world');[...map] //[["name","hello"],[{},"world"]]//数组转Mapconst map = new Map([["name","hello"],[{},"world"]]);map // {"name" => "hello", Object {} => "world"}

Promise 对象

  1. Promise对象是ES6对异步编程的一种解决方案,它有以下两个特点:

Promise对象代表一个异步操作,它只有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败),并且该状态不会受外界的影响
Promise对象的状态改变,只有两种可能:从 Pending 变为 Resolved 或者从 Pending 变为 Rejected,并且一旦状态改变,就不会再变,任何时候都可以得到这个结果

  1. Promise对象的一些缺点:

一旦新建了一个Promise对象,就会立即执行,并且无法中途取消

let promise = new Promise(function(resolve, reject) {    console.log('Promise');    resolve();});// Promise

如果不设置Promise对象的回调函数,Promise会在内部抛出错误,不会反应到外部,也就是在外部拿不到错误提示
如果Promise对象处于Pending状态时,是无法得知捕获进展到哪一个阶段的(刚刚开始还是即将完成)

  1. Promise对象是一个构造函数,用来生成Promise实例,下面是创造了一个Promise实例的示例
let promise = new Promise(function(resolve, reject) {  // ... to do  if ( success ){    resolve(value);    //成功操作  } else {    reject(error);     //失败操作  }});

ps:Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject ,分别用来处理成功和失败的回调;

  1. Promise实例生成以后,可以用 then 方法分别指定 Resolved 状态和 Reject 状态的回调函数;
promise.then(function(value) {  // success}, function(error) {  // failure});

ps:then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Rejected时调用,其中,第二个函数是可选的;

  1. resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例,表示异步操作的结果有可能是一个值,也有可能是另一个异步操作;
let promise1 = new Promise(function (resolve, reject) {  // ...});let promise2 = new Promise(function (resolve, reject) {  // ...  resolve(p1);})

上面代码表示一个异步操作的结果是返回另一个异步操作,promise1 的状态就会传递给 promise2 , 如果 promise1 的状态是Pending,那么 promise2 的回调函数就会等待promise1的状态改变;如果promise1的状态已经是Resolved或者Rejected,那么promise2的回调函数将会立刻执行;

  1. Promise实例方法then返回的是一个新的Promise实例,因此可以采用链式写法,即then方法后面再调用另一个then方法
let promise = new Promise(function (resolve, reject) {  // ...})promise.then(function(res) {   return res.post;}).then(function(post) {   // ...});

ps:上例中依次指定了两个回调函数,第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数,如果返回的是 Promise 对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用

let promise = new Promise(function (resolve, reject) {  // ...})promise.then(function(res) {   return new Promise(/.../);}).then(function(res) {   // Resolved},function(error){   // Rejected});
  1. Promise.prototype.catch 方法用于指定发生错误时的回调函数,不仅异步操作抛出错误(即状态就会变为Rejected),而且 then 方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获
let promise = new Promise(function(resolve, reject) {    throw new Error('test');}).catch(function(error) {  console.log(error);});// Error: test
  1. 如果Promise状态已经变成Resolved,再抛出错误是无效的
let promise = new Promise(function(resolve, reject) {  resolve('ok');  throw new Error('test');});promise  .then(function(value) { console.log(value) })  .catch(function(error) { console.log(error) });  //ok

ps: 出现上述结果是由于 之前提到的 Promise 的状态一旦改变,就永久保持该状态,不会再变了,因此在 resolve 语句后面,再抛出错误,是不会被捕获的

  1. Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止,因此建议总是使用catch方法,而不使用then方法的第二个参数,因为使用catch方法可以捕获前面then方法执行中的错误
// badpromise  .then(function(data) {    // success  }, function(err) {    // error  });// goodpromise  .then(function(data) { //cb    // success  })  .catch(function(err) {    // error  });
  1. Promise.all方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,该方法接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。
var p1 = new Promise(function (resolve) {    setTimeout(function () {        resolve("Hello");    }, 3000);});var p2 = new Promise(function (resolve) {    setTimeout(function () {        resolve("World");    }, 1000);});Promise.all([p1, p2]).then(function (result) {    console.log(result);    // ["Hello", "World"]});

上面的例子模拟了传输两个数据需要不同的时长,虽然 p2 的速度比 p1 要快,但是 Promise.all 方法会按照数组里面的顺序将结果返回,但 promise 本身并不是一个个的顺序执行的,而是同时开始、并行执行的,可以利用这个特点处理需要多个回调返回后才能进行的操作

  1. Promise.race方法和Promise.all方法类似,也接收一个promise对象数组为参数,不同的是只要该数组中的 Promise 对象的状态发生变化(无论是 resolve 还是 reject)该方法都会返回。
var p1 = new Promise(function (resolve) {    setTimeout(function () {        resolve("Hello");    }, 3000);});var p2 = new Promise(function (resolve) {    setTimeout(function () {        resolve("World");    }, 1000);});Promise.race([p1, p2]).then(function (result) {    console.log(result);    // Wrold});
  1. 一般情况下我们都会使用 new Promise() 来创建promise对象,除此之外,可以使用 Promise.resolve 和 Promise.reject这两个方法;

静态方法Promise.resolve(value) 可以认为是 new Promise() 方法的快捷方式

let promise = Promise.resolve('resolved');//等价于let promise = new Promise(function(resolve){    resolve('resolved');});

上述的promise对象立即进入确定(即resolved)状态,并将 ‘resolved’ 传递给后面then里所指定的 onFulfilled 函数。

Promise.resolve('resolved').then(function(value){    console.log(value);});// resolved

Promise.reject(error)是和 Promise.resolve(value) 类似的静态方法,是 new Promise() 方法的快捷方式。

let promise = Promise.reject(new Error("出错了"));//等价于let promise = new Promise(function(resolve,reject){    reject(new Error("出错了"));});

上述 promise 对象通过then指定的 onRejected 函数,并将错误(Error)对象传递给这个 onRejected 函数

Promise.reject(new Error("fail!")).catch(function(error){    console.error(error);});// Error : fail!
  1. 我们可以利用 Promise 应用到我们实际开发中,下面举几个栗子
//图片加载const preloadImage = function (path) {  return new Promise(function (resolve, reject) {    var image = new Image();    image.onload  = resolve(image);    image.onerror = function() {        reject(new Error('Could not load image at ' + path));    };    image.src = path;  });}//文件读取function reader (file) {  return new Promise(function (resolve, reject) {    let reader = new FileReader();    reader.onload = function () {      resolve(reader);    };    reader.onerror = function() {        reject(new Error('Could not open the file ' + file));    };    if (!file.type || /^text\//i.test(file.type)) {      reader.readAsText(file);    } else {      reader.readAsDataURL(file);    }  })}
原创粉丝点击