ES6 常用

本贴最后更新于 1637 天前,其中的信息可能已经时异事殊

一、let 和 const

1、let 可以声明变量

let name = '张三';
console.log(name);//张三

2、ES6 新增块级作用域

  注:在 ES6 之前作用域分为:全局作用域、函数作用域

{
var name1 = '张三';
let name2 = '李四';
console.log(name2);//李四
}
console.log(name1);//张三
console.log(name2);//报错

  上面代码在代码块之中,分别用 let 和 var 声明了两个变量。然后在代码块之外调用这两个变量,结果 let 声明的变量报错,var 声明的变量返回了正确的值。而在代码块内 let 声明的变量返回了正确的值。这表明,let 声明的变量只在它所在的代码块有效。

3、let 的使用场景


var a =[]; for(var i=0;i<10;i++){
    a[i] = function(){
        console.log(i);
    }
}
a[6]();//10

  上面的代码中,变量 i 是 var 声明的,在全局范围内都有效。所以每一次循环,新的 i 值都会覆盖旧值,导致最后输出的是最后一轮的 i 的值。


var a = []; for(let i=0;i<10;i++){
    a[i]=function(){
        console.log(i);
    }
}
a[6]();//6

  上面代码中,变量 i 是 let 声明的,声明的变量仅在块级作用域内有效,当前的 i 只在本轮循环有效,所以每一次循环的 i 其实都是一个新的变量,最后输出的是 6.

4、不存在变量提升

console.log(a);
console.log(b); var a = 2;//undefined
let b = 3;//报错

  上面代码中,变量 a 用 var 命令声明,会发生变量提升,即脚本运行时,变量 a 已经存在了,但是么有值,所以会输出 undefined。变量 b 用 let 命令声明,不会发生变量提升。这表明在声明它之前,变量 b 是不存在的,这时如果用到它,就会抛出一个错误。

5、暂时性死区

let b = 3 {
    b=4 }
console.log(b);//4

  当代码执行到代码块中时,b=4 在代码块中是没有定义的,所以它会往外找定义的变量,找到 let b=3;那么 b=4 就会覆盖 b=3,所以最后 b=4


let b=3 {
    b=4;
    let b=5;
}
console.log(b);//报错

  上面代码中,存在全局变量 b,但是块级作用域内又声明了一个局部变量 b,导致后者绑定这个块级作用域,不会再向块区外面找,所以在 let 声明变量前,对 b 赋值就会报错。暂时性死区的本质就是,只要一进入当前作用域,所要使用的变量就已经存在了,但是不可获取,只有等到声明变量的那一行代码出现,才可以获取和使用该变量。

  ES6 明确规定,如果区块中存在 let 和 const 命令,这个区块对这些命令声明的变量,从一开始就形成看封闭作用域。凡是在声明之前就使用这些变量,就会报错。总之,在代码块内,使用 let 命令声明变量之前,该变量都是不可用的。在语法上,称为“暂时性死区”。

6、不允许重复声明

{
    let a = 10;
    let a = 8;
    console.log(a);//报错
}

  let 不允许在相同的作用域内重复声明同一个变量。

7、顶层对象属性

var a = 2;
console.log(window.a);//2
 let b = 3;
console.log(window.b);//undefined

  顶层对象,在浏览器环境指的是 window 对象,在 Node 指的是 global 对象。ES5 之中,顶层对象的属性与全局变量是等价的。

  ES6 规定,var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性;let 命令、const 命令、class 命令声明的全局变量、不属于顶层对象属性。 所以上面代码中,全局变量 b 是由 let 声明 ,不是顶层对象,返回 undefined。

8、const

  上面介绍 let 有的属性,const 都有;唯一的区别是 const 声明的变量是不可以修改的,const 声明一个只读的常量,一旦声明,常量的值就不能改变。

const a =2;
a=3;
console.log(a);//报错

  const 特殊的场景

const a ={name:'张三'};
a.name = '李四';
console.log(a);//李四

  上面代码中,常量 a 储存的是一个地址,这个地址指向一个对象,不可变的只是这个地址,即不能把 a 指向另一个地址,但对象本身是可变的。

二、变量解构赋值

  ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。

1、模式匹配

  模式匹配:只要等号俩边的模式相同,左边的变量就会被赋予对应的值。下面是解构列子


let [foo,[[bar],baz]]=[1,[[2],3]];
console.log(foo);//1
console.log(bar);//2
console.log(baz);//3
 let [ , , third]=['a','b','c'];
console.log(third);//c
 let [x, ,y]=[5,6,7];
console.log(x);//5
console.log(y);//7
 let [head,...tail]=[1,2,3,4];
console.log(head);//1
console.log(tail);//[2, 3, 4]
 let [k,j,...z]=['a'];
console.log(k);//a
console.log(j);//undefined 
console.log(z);//[] //注:如果解构不成功,变量的值就等于undefined。

2、不完全解构

  即等号左边的模式,只匹配一部分的等号右边的数组,这种情况下,解构依然可以成功。


let [x,y]=[1,2,3];
x //1
y //2
 let [a,[b],c]=[1,[2,3],4];
a//1
b//2
c//4 //注:var,let,const命令都适用解构赋值

3、默认值

[x,y='b']=['a'];
console.log(x);//a
console.log(y);//b //解构赋值允许指定默认值

4、对象解构赋值

var {foo,bar}={foo:'aaa',bar:'bbb'};
console.log(foo);//aaa
console.log(bar);//bbb

  注:对象解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。对象解构也可以指定默认值。

5、函数参数解构赋值

function add([x,y]){ return x+y;
}
add([1,2]);//3

  上面代码中,函数 add 的参数表面上是一个数组,但在传入参数的那一刻,数组参数就被解构成变量 x 和 y。对于函数内部的代码来说,它们能感受的参数就是 X 和 Y。


//函数参数的解构也可以使用默认值
function move({x=0,y=0}={}){ return [x,y];
}
move({x:3,y:8});//[3,8]
move({x:3});//[3,0]
move({});//[0,0]
move();//[0,0]

  上面代码中,函数 move 的参数就是一个对象,通过对这个对象就行解构,得到变量 x 和 y 的值,如果解构失败,x 和 y 等于默认值。

三、字符串扩展

1、for...of 循环遍历


var str ='html'; for(let i of str){
    console.log(i);
} //h //t //m //l //for of遍历将字符串每个字符都遍历出来了

2、includes()、startsWith()、endsWith()

  includes():返回布尔值,表示是否找到了参数字符串。
  startsWith():返回布尔值,表示参数字符串是否在源字符串的头部。
  endsWith():返回布尔值,表示参数字符串是否在源字符串的尾部。

var str = 'Hello world!';
str.stsrtsWith('world',6)//true
str.endsWith('Hello',5)//true
str.includes('Hello',6)//false

  上面代码表示,使用第二个参数 n 时,endsWith 的行为与其他俩个方法有所不同,它针对前 n 个字符,而其他俩个方法针对从第 n 个位置直到字符串结束。

3、repeat()

//repeat方法返回一个新字符串,表示将原字符串重复n次。
'x'.repeat(3);//'xxx'
'hello'.repeat(2);//'hellohello'

4、模板字符串

  模板字符串:是增强版的字符串,用反引号(`)标识,它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量,模板字符串中嵌入变量,需要将变量名写在 ${}之中。

var name ='Bob',time='today';
`hello ${name},how are you ${time}?`

四、数值扩展

1、二进制和八进制的表示法

  ES6 提供了二进制和八进制数值的新写法,分别用前缀 0b 和 0o 表示。

2、Number.isNaN()、Number.parseInt()、Number.parseFloat()


//Number.isNaN()用来检查一个值是否为NaN
Number.isNaN(NaN);//true
Number.isNaN(15);//false
Number.isNaN('15');//false
console.log(Number.isNaN(true));//false  
//它们与传统的全局方法isNaN()的区别在于,传统方法先调用Number()将非数值的值转为数值,再进行判断,而该新方法只对数值有效,非数值一律返回false.  

  


//ES6将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。
//ES5写法
parseInt('12.34');//12
parseFloat('16.88#');//16.88

//ES6写法
Number.parseInt('12.34');//12
Number.parseFloat('16.88#');//16.88 //这样做的目的,是逐步减少全局性方法,使得语言逐步模块化

3、Math 对象的扩展

  Math.trunc():去除一个数的小数部分,返回整数部分


Math.trunc(4.1);//4
Math.trunc(4.9);//4
Math.trunc(NaN);//NaN
Math.trunc('foo');//NaN
Math.trunc();//NaN //对于非数值,Math.trunc内部使用Number方法将其先转为数值,对于空值和无法截取整数的值,返回NaN.

  

  Math.sign():用来判断一个数到底是正说、负数、还是零。


// 它会返回五中值:参数为正数,返回1;参数为负数,返回-1;参数为0,返回0;参数为-0,返回-0;其他值,返回NaN.
Math.sign(-5);//-1
Math.sign(5);//+1
Math.sign(0);//+0
Math.sign(-0);//-0
Math.sign(NaN);//NaN
Math.sign('foo');//NaN

  

  Math.cbrt():用于计算一个数的立方根

Math.cbrt(8);//2
Math.cbrt('27');//3
Math.cbrt('hello');//NaN //对于非数值,Math.cbrt方法也是先使用Number方法将其转为数值。

  Math.hypot():返回所有参数的平方和的平方根

Math.hypot(3,4);//5
Math.hypot(3,'4');//5
Math.hypot(3,4,'foo');//NaN //如果参数不是数值,Math.hypot方法将其转为数值,只要有一个参数无法转为数值,就会返回NaN.

五、数组扩展

1、Array.form()

//Array.form():用于将俩类对象转为真正的数组:类似数组的对象和可遍历的对象
var oList = document.getElementById('list');
var aLi = Array.from(oList.children); 
//实际应用中,常见的类似数组的对象是DOM操作返回的NodeList集合,以及函数内部的arguments对象。Array.from都可以将它们转为真正的数组。

2、数组实列 find()和 findIndex()

  数组实例的 find 方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined。


var arr =[1,5,6,9]; var result = arr.find(function(v){ if(v>5){ return true;
    }else{ return false;
    }
});
console.log(result);//6

  数组实例的 findIndex 方法的用法与 find 方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。


var arr =[1,5,6,9]; var result = arr.find(function(v){ if(v>5){ return true;
    }else{ return false;
    }
});
console.log(result);//2

六、函数扩展

1、函数参数默认值


// ES6允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x,y='world'){
    console.log(x,y);
}
log('hello');//hello world
log('hello',china);//hello china
log('hello','')//hello

2、参数变量是默认声明的,不能用 let 或 const 再次声明。

function foo(x=5){
    let x =1;//error
    const x=2;//error
} // 上面代码中,参数变量x是默认声明的,在函数体中,不能用let或const再次声明,否则会报错。

3、rest 参数

  ES6 引入 rest 参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用 arguments 对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

function print(a,...b){
    console.log(a,b);
}

print(4,5,6,7,8,);//4,[5,6,7,8]

4、箭头函数

1.箭头函数简化了函数的书写方式,使得书写更加优雅;

2.箭头函数绑定了上下文的 this,使得 this 指向符合预期,而不必使用 _this = this 或.bind(this)获得预期的 this 值

5、严格模式

  从 ES5 开始,函数内部可以设定为严格模式。《ECMAScript 2016 标准》做了一点修改,规定只要函数参数使用了默认值、解构赋值、或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。

6、箭头函数


//ES6允许使用“箭头”(=>)定义函数
var f = v =>v; // 上面的箭头函数等同于
var f = function(v){ return v;
} //如果箭头函数不需要参数或需要多个参数,就使用一个圆括号代表参数部分。
var sum =(num1,num2)=>num1+num2; //等同于
var sum =function(num1,num2){ return num1+num2;
} // this在箭头函数中的使用
1:在普通函数中,this指向的是window,在严格模式下,this指向的是undefined 2:在方法内,this指向的是方法的拥有者 3:在箭头函数内,this指向的是创建箭头函数时所在的环境中this指向的值。

document.onclick=ev=>{
 
    alert(ev.clientX+','+ev.clientY);
 
};
 

let sum=(a,b)=>a+b;
 
alert(sum(12, 88)); // 100

当箭头函数不加{}时, 箭头后的表达式默认为返回值

七、对象扩展

1、属性的简洁表示法


//ES6允许直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁
function f(x,y){ return{x,y};
} //等同于
function f(x,y){ return{x:x,y:y};
}
f(1,2);//{x:1,y:2}

//同名属性可以缩写:

let obj = {

name// 等同于name:name

}

2、Object.assign()


//用于对象的合并,将源对象(source)的所有属性,复制到目标对象(target)
var target ={a:1}; var sourcel={b:2}; var sourde2={c:3};
Object.assign(target,sourcel,sourde2);
target //{a:1,b:2,c:3}

  Object.assign 方法的第一个参数是目标对象,后面的参数都是源对象。注意,如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。

Object.assign(target,source1,source2);  

(1)Object.assign 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target);

(2)目标对象有同名属性会覆盖源对象;

(3)如果该参数不是对象,则会先转成对象,然后返回;

(4)如果 undefined 和 null 在 target 参数位置则报错,否则转换不了对象而跳过;

(5)Object.assign 方法实行的是浅拷贝,而不是深拷贝;

常见用途:

(1)为对象添加属性

(2)为对象添加方法

(3)克隆对象 Object.assign({},origin)

(4)合并多个对象 Object.assign(target,...sources)

(5)为属性指定默认值 Object.assign({},DEFAULTS,options)

3、Object.is

Object.is() 解决=== 的两个问题;

ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的 NaN 不等于自身,以及 +0 等于-0


Object.is(+0,-0) // false

Object.is(NaN,NaN) // true

4、Object.getOwnPropertyDescriptors

Object.getOwnPropertyDescriptors()

返回某个对象属性的描述对象(descriptor)

5、__proto__属性,Object.setPrototypeOf(),Object.getPrototypeOf()

__proto__调用的是 Object.prototype.proto,无论从语义的角度,还是从兼容性的角度,都不要使用这个属性,而是使用下面的 Object.setPrototypeOf()(写操作)、Object.getPrototypeOf()(读操作)、Object.create()(生成操作)

6、Object.keys(),Object.values(),Object.entries()

ES5 引入了 Object.keys 方法,返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键名。ES2017 引入了跟 Object.keys 配套的 Object.values 和 Object.entries,作为遍历一个对象的补充手段,供 for...of 循环使用。

八、Set 和 Map

1、Set

  ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。Set 本身是一个构造函数,用来生成 Set 数据结构。


var oSet = new Set([1,3,7]);
oSet.add(4);
oSet.add(7);
console.log(oSet); // 数组去重
var arr = [1,4,5,7,7,5,4,1]; // (方法一)Array.from方法可以将Set结构转为数组
console.log(Array.from(new Set(arr))); // (方法二)
console.log([...new Set(arr)]);

 

  Set 实例属性

// constructor属性:构造函数,默认就是Set函数。 // size属性:返回Set实例的成员总数。
var oSet = new Set([2,5,8]);
console.log(oSet.constructor);
console.log(oSet.size);

  Set 实列方法

  Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。

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

//(遍历方法) //keys(),values(),entries():keys方法、values方法、entries方法返回的都是遍历器对象。  
//由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let set = new Set(['red','green','blue']); for(let item of set.keys()){
    console.log(item);
} // red // green // blue

for(let item of set.values()){
    console.log(item);
} // red // green // blue

for(let item of set.entries()){
    console.log(item);
} //['red','red'] //['green','green'] //['blue','blue'] //上面代码中,entries方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等。

2、Map

  JavaScript 的对象(Object),本质上是键值对的集合,但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应。如果你需要“键值对”的数据结构,Map 比 Object 更合适

var o = new Map();
o.set('name','张三');
o.set('function(){}','567');

  

  Map 实列的属性和方法 


//size属性:返回Map结构的成员总数。  
//set(key, value):set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。set方法返回的是Map本身,因此可以采用链式写法。  
//get(key):get方法读取key对应的键值,如果找不到key,返回undefined  
//has(key):返回一个布尔值,表示某个键是否在Map数据结构中。  
//delete(key):删除某个键,返回true。如果删除失败,返回false。  
//clear():清除所有成员,没有返回值。

九、Gennerator 生成器

  Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。从语法上,可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function 关键字与函数名之间有一个星号;二是,函数体内部使用 yield 语句,定义不同的内部状态(yield 语句在英语里的意思就是“产出”)。执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。


//generator生成器函数
function* sum(a,b){ var c = a + b;
    yield c; var d = c + a;
    yield d; var e = d + b;
    yield e; var f = c + d +e; return f;
} var o = sum(4,7);
console.log(o.next());//{value: 11, done: false}
console.log(o.next());//{value: 15, done: false}
console.log(o.next());//{value: 22, done: false}
console.log(o.next());//{value: 48, done: true}
console.log(o.next());//{value: undefined, done: true}

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

  yield 语句:由于 Generator 函数返回的遍历器对象,只有调用 next 方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield 语句就是暂停标志。
遍历器对象的 next 方法的运行逻辑如下:
  1:遇到 yield 语句,就暂停执行后面的操作,并将紧跟在 yield 后面的那个表达式的值,作为返回的对象的 value 属性值。
  2:下一次调用 next 方法时,再继续往下执行,直到遇到下一个 yield 语句。
  3:如果没有再遇到新的 yield 语句,就一直运行到函数结束,直到 return 语句为止,并将 return 语句后面的表达式的值,作为返回的对象的 value 属性值。
  4:如果该函数没有 return 语句,则返回的对象的 value 属性值为 undefined。

 next()方法:会执行 generator 的代码,然后,每次遇到 yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。返回的 value 就是 yield 的返回值,done 表示这个 generator 是否已经执行结束了。如果 done 为 true,则 value 就是 return 的返回值。当执行到 done 为 true 时,这个 generator 对象就已经全部执行完毕,不要再继续调用 next()了。

十、class 的写法

  俗话说物以类聚人以群分,具有一定的共同属性或特点的,就是 calss 来定义的类。


// 定义一个类
class Dog{
    constructor(name,age){ this.name = name; this.age  = age;
    }
    bark(){
        console.log('旺旺!')
    }
} var oDaHuang = new Dog('大黄',3); var oWangCai = new Dog('旺财',4);

console.log(oDaHuang);
console.log(oWangCai);

oDaHuang.bark();
oWangCai.bark(); // 上面代码表明,类的数据类型就是函数,类本身就指向构造函数。 // 使用的时候,也是直接对类使用new命令,跟构造函数的用法完全一致 // constructor方法:是类的默认方法,通过new命令生成对象实例时,自动调用该方法。一个类必须有constructor方法,如果没有显式定义,一个空的constructor方法会被默认添加。

  

  类的继承


//Class之间可以通过extends关键字实现继承 // 下面代码定义了一个类Dog,该类通过extends关键字,继承了Animal类的所有属性和方法。
 class Animal {
    constructor() { this.legs = 4;
    }
    eat() {
        console.log('吃东西!');
    }
}

class Dog extends Animal{
    constructor(name, age) {
        super(); // uper作为对象时,指向父类的原型对象。
        this.name = name; this.age  = age;
    }
    bark() {
        super.eat(); // super作为函数调用时,代表父类的构造函数。
        console.log('啊呜!');
    }
    sleep() {
        console.log('睡觉!');
    }
} var oDaHuang = new Dog('大黄', 3); var oWangCai = new Dog('旺财', 4);

oDaHuang.bark();

console.log(oDaHuang); // {legs: 4, name: "大黄", age: 3}
console.log(oWangCai); //{legs: 4, name: "旺财", age: 4}

  

super 关键字:
既可以当作函数使用,也可以当作对象使用。在这两种情况下,它的用法完全不同。

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

  注:作为函数时,super()只能用在子类的构造函数之中,用在其他地方就会报错

  第二种情况,super 作为对象时,指向父类的原型对象。

十一、Promises

Promise 是异步编程的一种解决方案,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

1.Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功,也可叫 resolved)和 rejected(已失败);

2.状态不受外界影响,且一旦状态改变,就不会再变;

缺点:

1.无法取消 Promise,一旦新建它就会立即执行,无法中途取消;

2.不设置回调函数,Promise 内部抛出的错误,不会反应到外部;

3.当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成);

用法:

1.ES6 规定,Promise 对象是一个构造函数,用来生成 Promise 实例;

2.Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 和 reject。它们是两个函数,由 JavaScript 引擎提供;

3.resolve 函数的作用是,将 Promise 对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

4.reject 函数的作用是,将 Promise 对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去;

5.Promise 实例生成以后,可以用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数。

6.then 方法可以接受两个回调函数作为参数。第一个回调函数是 Promise 对象的状态变为 resolved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受 Promise 对象传出的值作为参数。建议使用 then().catch(),而不是两个回调函数;

7.Promise.prototype.then()同第 6 点;

8.Promise.prototype.catch() 这方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

9.Promise.prototype.finally() finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

10.Promise.all() 接受一个数组作为参数,数组里都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理;

例如:const p=Promise.all([p1,p2,p3]);

(1)只有 p1、p2、p3 的状态都变成 fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数;

(2)只要 p1、p2、p3 之中有一个被 rejected,p 的状态就变成 rejected,此时第一个被 reject 的实例的返回值,会传递给 p 的回调函数。

11.Promise.race()

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

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

12.Promise.resolve() 将现有对象转为 Promise 对象,有 4 条规则;

13.Promise.reject() Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为 rejected。

14.Promise.try() 不知道或者不想区分,函数 f 是同步函数还是异步操作,但是想用 Promise 来处理它。

应用:


const preloadImage=function(path){
	return new Promise(function(resolve,reject){
		const image=new Image();
		image.onload=resolve;
		image.onerror=reject;
		image.src=path;
	})
};

十二、modules

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。

1、ES6 的模块自动采用严格模式

2、export 命令

export var firstName='Michael';

或 var firstName='Michael'; export{firstName};

export function multiply(x,y){return x*y;};

export 输出的变量就是本来的名字,但是可以使用 as 关键字重命名

export 命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系,而不能导出一个值,如 export m;

export 语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

export 命令可以出现在模块的任何位置,只要处于模块顶层就可以。

3、import 命令

import 命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块对外接口的名称相同;

可以使用 as 关键字,将输入的变量重命名;

import 命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口;

import 命令具有提升效果,会提升到整个模块的头部,首先执行;

import 是静态执行,所以不能使用表达式和变量;

import 语句会执行所加载的模块,多次重复执行同一句 import 语句,那么只会执行一次,而不会执行多次;

import 可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面,import * as xxx from 'xxx';

4、export default 命令

export default 就是输出一个叫做 default 的变量或方法,然后系统允许你为它取任意名字,故 import 的时候可以随意取名字;

export default 也可以用来输出类。

5、export 与 import 的复合写法

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

export{foo as myFoo} from 'my_module';// 接口改名

6、import()动态加载

import 命令能够接受什么参数,import()函数就能接受什么参数,两者区别主要是后者为动态加载;

(1)按需加载

(2)条件加载

(3)动态的模块路径

十三、Spread Operator ... 展开运算符/扩展运算符

ES6 中另外一个好玩的特性就是 Spread Operator 也是三个点儿...接下来就展示一下它的用途。

组装对象或者数组

    //数组
    const color = ['red', 'yellow']
    const colorful = [...color, 'green', 'pink']
    console.log(colorful) //[red, yellow, green, pink]
    
    //对象
    const alp = { fist: 'a', second: 'b'}
    const alphabets = { ...alp, third: 'c' }
    console.log(alphabets) //{ "fist": "a", "second": "b", "third": "c" }

有时候我们想获取数组或者对象除了前几项或者除了某几项的其他项

    //数组
    const number = [1,2,3,4,5]
    const [first, ...rest] = number
    console.log(rest) //2,3,4,5
    //对象
    const user = {
        username: 'lux',
        gender: 'female',
        age: 19,
        address: 'peking'
    }
    const { username, ...rest } = user
    console.log(rest) //{"address": "peking", "age": 19, "gender": "female" }

对于 Object 而言,还可以用于组合成新的 Object 。(ES2017 stage-2 proposal) 当然如果有重复的属性名,右边覆盖左边

    const first = {
        a: 1,
        b: 2,
        c: 6,
    }
    const second = {
        c: 3,
        d: 4
    }
    const total = { ...first, ...second }
    console.log(total) // { a: 1, b: 2, c: 3, d: 4 }
//扩展运算符:扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。  
console.log(...[1,2,3]);//1 2 3  
console.log(1,...[2,3,4],5);//1 2 3 4 5  
//扩展运算符应用一 // 合并数组  
 [1,2].concat(more);//ES5  
[1,2,...more];//ES6  
var arr1 = ['a','b']; var arr2 = ['c']; var arr3 = ['d','e']; //ES5的合并数组  
arr1.concat(arr2,arr3);//['a','b','c','d','e'] //ES6的合并数组  
[...arr1,...arr2,...arr3];//['a','b','c','d','e']   
  
  
//扩展运算符应用二 //与解构赋值结合  
const[first,...rest]=[1,2,3,4,5];  
first//1  
rest//[2,3,4,5]  
  
// 如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。  
const[...first,last]=[1,2,3,4,5];//报错  
const[first,...middle,last]=[1,2,3,4,5];//报错  
//扩展运算符应用二 //字符串:扩展运算符还可以将字符串转为真正的数组。  
[...'hello']//['h','e','l','l','o']  

十四、async 函数

es6 引入了 async 函数,使得异步操作变得更加方便。

1、async 函数是什么?

一句话,它就是 Generator 函数的语法糖。

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 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await,仅此而已。

async 函数对 Generator 函数的改进,体现在以下四点。

  • 内置执行器
  • 更好的语义
  • 更广的适用性
  • 返回值是 Promise

2、await 语句

  1. await 后面是一个 promise 对象,如果不是,会转成一个 resolve 的 promise 对象
      async function f() {
          return await 123;
      }
      f().then(function (a) {
          console.log(a);
      })

  1. await 后面的 promise 对象,如果 reject,则 reject 参数会被 cache 参数接收到,写不写 return 都可以,并且 reject 下面的代码不会执行,如果想下面的代码执行,必须用 try cache 包住
      async function a() {
           try{
              await Promise.reject('出错了!')
           }catch (e){
              return await Promise.resolve('请重新填写') 
           }
      }
      a().then(function () {
         console.log(err);
      }).catch(function (err) {
          console.log(err);
      })

上面的代码如果不用 try cache 包裹 reject,则下面的代码不会执行,并且 reject 语句是不用 return 返回的,resolve 语句是需要用 return 返回;

3、async 错误处理常用写法

如果 await 后面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被 reject。所以通常的处理方法有两种

  1. 用 try catch 包住可能会出错的部分
      async function myFunction() {
          try {
              await somethingThatReturnsAPromise();
          } catch (err) {
              console.log(err);
          }
      }
  1. 另一种写法是对可能要出错的异步操作添加 catch 回调函数
   async function myFunction() {
           await somethingThatReturnsAPromise().catch((err)=> {
                   console.log(err);
           })
       }

4、多个 await 语句同时触发

  let a=await getFoo();
  let b=await getBar();

上面的两个方法都是异步操作,不存在依赖关系,所以我们可以同时触发,改写成下面的

  //第一种写法
  let [a,b]=Promise.all([getFoo(),getBar()])
  //第二种写法
  let aPromise=getFoo();
  let bPromise=getBar();
  let a=await aPromise;
  let b=await bPromise;

5、async 函数的实现原理

async 函数就是将执行器和 Generator 做为一个函数返回。

  async function fn(){}
  //等同于
  function fn() {
    return spawn(function* () {
      
    })
  }
  function spawn(genF) {
          /****
           * 返回的是一个promise
           */
          return new Promise(function(resolve, reject) {
              var gen=genF(); //运行Generator这个方法;
              /***
               * 执行下一步的方法
               * @param fn 一个调用Generator方法的next方法
               */
             function step(fn) {
                  //如果有错误,则直接返回,不执行下面的await
                 try {
                  var next=fn();
                 }catch (e){
                   return reject(e)
                 }
                  //如果下面没有yield语句,即Generator的done是true
                 if(next.done){
                    return resolve(next.value);
                 }
                 // 如果下面还有yield语句,resolve继续执行递归执行gen.next(),reject则抛出错误
                 Promise.resolve(next.value).then((val)=>{
                     step(function(){ return gen.next(val) } )
                 }).catch((e)=>{
                     step(function(){ return gen.throw(e) } )
                 })
             }
              step(function () {
                 return gen.next();
             })
          });
      }
  • ES6
    10 引用 • 6 回帖 • 1 关注
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    710 引用 • 1173 回帖 • 175 关注

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
gjTool
欢迎博客互链 https://www.gjtool.cn/ 武汉