ECMAScript 6.0(简称ES6)是javascript下一代标准,它的目标是使javascript可以编写大型复杂的应用程序,成为企业级开发语言。
异步方案
- Promise
- async/await
- Generator(生成器)
Promise
解决回调地狱(如ajax请求根据上一次请求结果做下一次请求,层层回调,难以维护) 三种状态
- pending
- rejected
- resolved
实例方法
- then(resolve, reject)接受成功和失败两个回调函数。可以不写reject回调,在catch方法中捕获异常信息
- catch()捕获异常信息
- finally()成功与否都会执行
对象方法
- all()
传入一个promise数组,所有promise都成功,才会之行resolve回调,只要有一个失败,执行reject,返回第一个执行失败的结果。
Promise.all([pro1, pro2, pro3]).then(function(res){
console.log(res) // res为数组,分别几个promise执行成功的结果
}, function(err){
console.log(err) // err为第一个失败结果
})
- race()
同样传入一个promise数组,一旦有一个promise执行完毕,就返回结果。
let a = new Promise(function(resolve, reject){
setTimeout(() => {
reject(1)
}, 1000);
})
let b = new Promise(function(resolve, reject){
setTimeout(() => {
resolve(2)
}, 2000);
})
let c = new Promise(function(resolve, reject){
setTimeout(() => {
resolve(3)
}, 3000);
})
Promise.race([a, b, c]).then(function(res) {
console.log(res, 'resolved')
}, function(err) {
console.log(err, 'err') //1 'err'
})
手撕Promise
// TODO
async/await
采用同步思想处理异步代码
-
await只能在async函数内部使用,否则会报语法错误
-
await阻塞程序,直到promise执行resolve后才会继续执行
-
自动将常规函数转换成Promise(即使函数内部返回非promise, 也会包装成promise),返回值也是一个Promise对象
-
如果async函数return一个值,这个值就是Promise对象中resolve的值,若无return,这个值为undefined
-
如果async函数throw一个值,这个值就是promise对象种reject的值
async函数写法:
async function imAsync(num) {
if (num > 0) {
return num // 这里相当于resolve(num)
} else {
throw num // 这里相当于reject(num)
}
}
imAsync(1).then(function (v) {
console.log(v); // 1
});
// 注意这里是catch
imAsync(0).catch(function (v) {
console.log(v); // 0
})
Promise写法:
function imPromise(num) {
return new Promise(function (resolve, reject) {
if (num > 0) {
resolve(num);
} else {
reject(num);
}
})
}
imPromise(1).then(function (v) {
console.log(v); // 1
})
imPromise(0).then(function (v) {
console.log(v);
}, function(v){
console.log(v); // 0
})
错误处理
在async函数里,无论是Promise reject的数据还是逻辑报错,都会被默默吞掉,所以最好把await放入try{}catch{}中,catch能够捕捉到Promise对象rejected的数据或者抛出的异常
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {reject('error')}, ms); //reject模拟出错,返回error
});
}
// 方式一
async function asyncPrint(ms) {
try {
console.log('start');
await timeout(ms); //这里返回了错误
console.log('end'); //所以这句代码不会被执行了
} catch(err) {
console.log(err); //这里捕捉到错误error
}
}
asyncPrint(1000);
// 方式二
async function asyncPrint(ms) {
console.log('start');
await timeout(ms)
console.log('end'); //这句代码不会被执行了
}
asyncPrint(1000).catch(err => {
console.log(err); // 从这里捕捉到错误
});
// 方式三
async function asyncPrint(ms) {
console.log('start');
await timeout(ms).catch(err => { // 注意要用catch
console.log(err)
})
console.log('end'); //这句代码会被执行
}
asyncPrint(1000);
Generator
执行生成器函数,会返回一个迭代器对象(Iterator) 特征:
- function关键字与函数名之间有一个星号
- 函数内部使用yield标志内部状态
Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。调用return方法,会结束遍历并返回传入的值
function* gen() {
yield 1;
yield 2;
yield 3;
return 'ending';
}
let g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.return(5)); // { value: 5, done: true }
console.log(g.next()); // { value: undefined, done: true }
Symbol
基本数据类型,用来表示独一无二的值,它是JavaScript中的第七种数据类型,与undefined、null、Number(数值)、String(字符串)、Boolean(布尔值)、Object(对象)并列。
// 创建一个symbol值
const name = Symbol();
console.log(name); //Symbol()
//只能转化为字符串和布尔值
console.log(String(name)); //Symbol()
console.log(Boolean(name)); // true
console.log(Number(name)) //error: Cannot convert a Symbol value to a number
// symbol值是唯一的,传参数没有意义仅用于辨别,即使传参一样,这两个变量也是不相等的
let a = Symbol('a');
let b = Symbol('b');
console.log(a === b) //false
- Symbol.for()
通过传入的参数(假设为x),全局查找symbol,如找到返回该symbol。否则使用给定的key(x)在全局创建一个新的symbol。
const a = Symbol.for("aa");
const b = Symbol.for("aa");
console.info("a===b", a === b); // a===b true
console.info("type", typeof a); // type symbol
- Symbol.keyFor()
用来获取全局 symbol 注册表中与某个 symbol 关联的键
let a = Symbol('hello');
let b = Symbol.for('world');
// 没有注册在全局
console.log(Symbol.keyFor(a)) // undefined
// 注册在全局因此能找到
console.log(Symbol.keyFor(b)) // world
- 用途:作为属性名
symbol变量名作为对象属性名,可以避免属性重复被覆盖和重写,symbol变量作为属性名有三种写法:
let symValue = Symbol("a");
// 通过 [] 添加 symbol 值属性
let obj1 = {};
obj[symValue] = "Hi";
// 定义含有该 属性 的对象
let obj2 = {
[symValue]: "Hi",
};
// 通过 defineProperty 添加该 symbol 属性
let obj3 = {};
Object.defineProperty(obj3, symValue, { value: "Hi" });
const / let
const
- 不允许重复声明
- 声明必须初始化
- 值不允许修改
- 块级作用域
let
- 不允许重复声明
- 块级作用域
- 不存在变量提升
class类
class语法糖,让对象原型的写法更加清晰、更像面向对象编程的语法。
- class声明类
- constructor定义构造函数初始化
- extends继承父类
- super调用父级构造方法
- static定义静态方法和属性
- 父类方法可以重写
变量解构赋值
// 数组解构
const age = [8, 7, 9];
let [Mike, Lucy, Jay] = age;
console.log(Mike, Lucy, Jay) // 8, 7, 9
// 对象解构
const Susan = {
age: 8,
sex: 'Male',
say: function() {
console.log('hello');
}
}
let {age, sex, say} = Susan
console.log(age) //8
say() // hello
箭头函数
- 如果形参只有一个,则小括号可以省略
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
- 箭头函数 this指向声明时所在作用域下 this 的值
- 箭头函数不能作为构造函数实例化
- 不能使用 arguments
rest参数
rest参数是一个数组
function add(...args) {
let sum = 0;
for(let val of args) {
sum += val;
}
return sum
}
add(2, 5, 3) // 10
rest必须是最后一个形参
function(...args, val) {} // error
函数的length属性不包括rest参数
function add(a, b, c, ...args) {}
console.log(add.length) // 3
扩展运算符Spread
Spread操作符可以展开Iterable的对象,所以除数组外,Set、Map、Generator等也可以使用。
var map=new Map();
map.set("x",1);
map.set("y",2);
var arr=[...map]; // [["x",1],["y",2]]
var set = new Set();
set.add(1);
set.add(2);
set.add(1);
set.add(3);
var arr = [...set]; //[1, 2, 3]
function *myGen() {
yield "hello";
yield "world";
}
var arr = [...myGen()]; //["hello", "world"]
模块化(import / export)
导入所有的
// main.js
import * as say from './say.js';
say.sayHi('John');
say.sayBye('John');
- 统一暴露(export {})
//暴露
const a =()=>{
console.log(999);
}
const b = 2
const c = 1
export{a,b,c}
//引入
import {a,b,c} from '@/api/api'
//使用
created(){
a()
console.log(b,c);
}
- 分别暴露(分别使用export)
//暴露
export const a =()=>{
console.log(999);
}
export const b = 2
export const c = 1
//引入
import {a,b,c} from '@/api/api'
//使用:可直接通过a,b,c获取
created(){
a()
console.log(b,c);
}
- 默认暴露(export default{})。
//暴露
const a =(data)=>{
return data+1
}
export default a
//引入
import a from '@/api/api'
//使用
created(){
console.log(a(5));
}
Set / Map数据结构
Set
是一种集合,由一堆无序的、相关联的且不重复的元素组成的组合。
- has(数据):判断set中是否存在对应的数据,返回true/false
- delete(数据):删除匹配数据
- clear():清空整个set集合
- size:获取set元素集合的元素数量(只读,不能赋值,赋值无效)
- add():向set末尾添加元素,如果该元素存在则不操作
遍历Set
- for...of...
- forEach遍历
// Set是一个构造函数
let set = new Set()
set.add(5);
set.add("5)
// set不会用强制类型转换来判断内部值是否重复
console.log(set.size) // 5
let set2 = new Set()
let key1 = {}
let key2 = {};
set2.add(key1);
set2.add(key2)
// 给set添加多个对象,引用类型是两个不同的项
console.log(set2.size) // 2
Object.is(+0,-0) // false,但是在set中,添加+0后再添加-0,则无效
set与数组相互转换
const set = new Set([1,2,3,4,5])
const arr = [...set] // set是一个可迭代对象
//数组利用set去重,再转数组
let arr=[11,22,33,44,55,11,22]
let newArr=[...new Set(arr)]
//字符串去重,再转字符串
let str='asdkjflkajsdlf'
let newStr=[...new Set(str)].join("")
Map
是一种叫做字典的数据结构,是键与相对应的值的集合。跟Object结构的区别在于Map的”键“不仅限于字符串,它可以是任何类型的值。
- has(key) :判断指定的键是否存在于 Map 中;
- delete(key) :移除 Map 中的键以及对应的值;
- clear() :移除 Map 中所有的键与值。
- size:包含项目个数
- set()
- get()
遍历Map
- for...of...
- forEach遍历
var m = new Map();
var o = { p: "Hello World" };
m.set(o, "content");
m.get(o); // "content"
m.has(o); // true
m.delete(o); // true
m.has(o); // false
WeakSet
- 只能是对象的集合,不像Set一样,可以是任何类型的集合。
- 不可枚举,没有size属性
- weakset中对象都是弱引用,如果外部没有引用该对象,那么垃圾回收制度会收回该对象所占用的内存,不管该对象是否还存在于weakset中。有助于防止内存泄露。
WeakMap
- 只接受对象作为键名(null除外)
- WeakMap的键名所引用的对象都是弱引用,一旦不再需要,键名对象和所对应的键值对会自动消失,不用手动删除引用。
- 不可遍历,无size属性,不支持clear方法