【前端】ECMAScript6从入门到进阶
1.ES6简介及环境搭建
1.1.ECMAScript 6简介
(1)ECMAScript 6是什么
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
(2)ECMAScript 和 JavaScript 的关系
ECMAScript是JavaScript的标准,JavaScript是ECMAScript的实现。换句话说,如果说JavaScript是一门语言的话,那么ECMAScript就是这门语言遵循的语法书。
(3)ES6 与 ECMAScript 2015 的关系
2011年的时候,ECMAScript 5.1版本发布,ECMA组织就开始准备6.0版本了。但因为这个版本引入的语法功能太多了,所以不可能通过一个版本就将所有功能涵盖。所以,标准委员会决定,标准在每年的6月份正式发布一次,作为当年的正式版本。接下来的时间,就在这个版本的基础上做改动,直到下一年的6月份,草案就自然变成了新一年的版本。这样一来,就不需要以前的版本号了,只要用年份标记就可以了。
就这样ES6的第一个版本就在2015.6正式发布了,正式名称就是《ECMAScript 2015标准》(简称ES2015)。
1.2.babel工具搭建ES6环境
采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。
- 首先确保环境中有node
node安装:http://nodejs.cn/download/
- 初始化工程项目
// 使用默认配置初始化项目
npm init -y
- 安装对应的依赖包
// 本地安装babel-cli及对应的转化规则
npm install --save-dev babel-cli babel-preset-es2015
// 阿里巴巴定制的镜像源
npm install -g cnpm --registry=https://registry.npm.taobao.org
-
创建项目目录
-
src目录用于存放编写的es6代码
-
dist目录用于存放由es6转化后的代码
-
-
配置babel
新建.babelrc
文件
// 配置用于转化当前代码的规则集
{
"presets": ["es2015"]
}
babel的基本用法
// -o 参数指定输出文件
node_modules/.bin/babel src/index.js -o dist/index.js
// -d 参数指定输出目录
node_modules/.bin/babel src -d dist
配置package.json文件实时编译
// 配置启动脚本的命令
"scripts": {
// 监听index.js文件,一发生变化就执行babel转译的命令
"dev": "babel src -w -d dist",
}
2.新的声明及数据赋值方式
2.1.变量声明let与const
作用域就是变量的有效范围,之前只有全局作用域和函数作用域,现在新增块级作用域。es6中新增let和const两种变量声明方式。
(1)let和const的相同点
- 只在声明的代码块内有效
if (true) {
var a = "a";
}
console.log(a);
// let声明变量,const同理
if (true) {
let b = "b";
}
console.log(b);
- 在同一作用域内不允许重复声明
- 没有变量提升,在声明变量前,不能使用该变量
console.log(a);
var a = 'a';
console.log(b);
let b = '1';
用var声明的变量会存在变量提升的问题,如果用let或者const就会消除变量提升问题,在遇到未定义的变量直接抛错。
(2)let和const的区别
- const用来声明一个只读的变量
const x = 10;
x = 13;
console.log(x);
注意:const声明的变量同时必须立即赋值,如声明的是简单类型的数据,变量的值不可改变。
2.2.ES6新数据类型Symbol
在ES6之前JavaScript的原始数据类型有Number、String、Boolean、Null、Undefined。ES6中增加新的Symbol独一无二的类型。
对象的属性名容易产生命名冲突,为保证键名的唯一性,故es6引入Symbol这种新的原始类型数据,确保创建的每个变量都是独一无二的。
- Symbol类型的数据是类似字符串的数据类型。
- 由于Symbol函数返回的值是原始类型的数据,不是对象,故Symbol函数前不能使用new命令,否则会报错。
let s = Symbol();
console.log(typeof s);
let s1 = Symbol();
let s2 = Symbol();
console.log(s1===s2);
let l1 = Symbol('lixiang');
let l2 = Symbol('lixiang');
console.log(l1===l2);
定义对象的唯一属性名
// 在对象里用Symbol作为属性名的三种写法
let s = Symbol();
// 第一种方式: 借助数组读取s变量,此时不能用点运算符,点运算符默认后面的参数是字符串
let a = {};
a[s] = 'lixiang'
// 第二种方式: 构造时声明
let a = {
[s]: 'lixiang'
}
// 第三种 Object.defineProperty
let a = {};
Object.defineProperty(a, s, { value: 'lixiang' });
定义常量
const s = Symbol("李祥");
2.3.解构赋值详解
(1)什么是解构赋值
解构赋值可以理解为赋值操作的语法糖,它是针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。代码书写上言简意赅,语义明确,也方便了对象数组的读取操作。
ES6中只要某种数据可以循环迭代,都可以进行解构赋值。
(2)数组解构
// 变量多时,对应不上的变成undefined
let [a, b, c] = [1, 2];
console.log(a, b, c);
// 变量默认值,声明变量时将c默认成6
let [a, b, c = 6] = [1, 2];
console.log(a, b, c);
// 分割数组,将1赋值给a,2,3成一个新数组
let [a, ...other] = [1, 2, 3];
console.log(a, other);
// 空内容,将a赋值为1,b赋值为3
let [a,,b] = [1, 2, 3];
console.log(a, b);
(3)对象解构
//1赋值为a,2赋值为b
let { a, b } = { a: 1, b: 2 };
console.log(a, b);
//重命名
let { a: aa, b: bb } = { a: 1, b: 2 };
console.log(aa, bb);
(4)读取接口返回的数据
function fun() {
return {
name: "张三",
hobby: [
{
title: "篮球",
},
{
title: "唱",
},
{
title: "RAP",
},
{
title: "跳",
},
],
};
}
let {
name,
hobby: [{ title }],
} = fun();
console.log(name, title);
3.ES6新增的数据操作方法
3.1.ES6提供的新的字符串方法
(1)新增的API
方法 | 描述 |
---|---|
includes(string, index) | 判断字符串中是否包含指定字符串,返回值是布尔值 |
startsWith(string, index) | 判断字符串的开头是否包含指定字符串,返回值是布尔值 |
endsWith(string, index) | 判断字符串的尾部是否包含指定字符串,返回值是布尔值 |
repeat(n) | repeat() 方法返回一个新字符串,表示将原字符串重复n 次。 |
padStart(length, str) | 字符串补齐,用于头部补全 |
padEnd(length, str) | 字符串补齐,用于尾部补全 |
//判断str是否包含an
let str = 'lixiang';
console.log(str.includes('an'));
//从下标为5的字符开始判断是否含an
console.log(str.includes('an',5));
//判断str是否以l开头
let str = 'lixiang';
console.log(str.startsWith('l'));
//从下标为3的字符开始判断是否以l开头
console.log(str.startsWith('l',3));
//判断str是否以g结尾
let str = 'lixiang';
console.log(str.endsWith('g'));
//从下标为3的字符开始判断是否以g结尾
console.log(str.startsWith('g',3));
//入参数字为2,重复两次
let str = 'lixiang';
console.log(str.repeat(2));
//在lixiang前面补a,补够10位
let str = 'lixiang';
console.log(str.padStart(10, 'a'));
//在lixiang后面补a,补够10位
let str = 'lixiang';
console.log(str.padEnd(10, 'a'));
(2)模板字符串
在ES5的时候拼接字符串我们往往这样写。
const name = '李祥';
const age = 18;
const hobbies = '写代码';
const str = '我叫'+name+',今年'+age+'岁,'+'喜欢'+hobbies+'。'
在ES6中新引入的模版字符串,写法如下。
const str = `我叫${name},今年${age}岁,喜欢${hobbies}。`
console.log(str);
(3)使用模板字符串的注意事项
- 使用模板字符串表示多行字符串时,所有的空格和缩进都会被保留在输出之中。
- 模板字符串中引入变量,要用 ${变量名} 这样的形式引入才可以。
- 模板字符串中的${…} 大括号内部可以放入任意的 JavaScript 表达式,可以进行运算、可以引用对象属性、可以调用函数、可以甚至还能嵌套,甚至还能调用自己本身。
3.2.ES6扩展运算符的使用
(1)深拷贝一维数组
const list = [1, 2, 3, 4, 5];
const listCopy = [...list];
console.log(listCopy);
(2)分割数组
const list = [1, 2, 3, 4, 5];
const [, ...list1] = list;
console.log(list1);
(3)将数组转化成参数传递给函数
const list = [1, 2];
function fun(a, b) {
console.log(a + b);
}
fun(...list);
3.3.ES6数组的扩展方法
(1)fill:替换数组中的元素,会改变原数组。想要不改变原数组的值,可以采用深拷贝。
const list = [1, 2, 3, 4, 5];
//全部替换成6
const list1 = [...list].fill(6);
//下标从1开始到下表为4之间左闭右开,全部替换成6
const list2 = [...list].fill(6, 1, 4);
console.log(list);
console.log(list1);
console.log(list2);
(2)find:查询数组中符合条件的元素。
const list = [
{ name: "李祥", age: 15 },
{ name: "张三", age: 10 },
{ name: "李四", age: 19 },
{ name: "王武", age: 20 },
];
const result = list.find(function (item) {
return item.name === "李祥";
});
console.log(result);
(3)findIndex:查询数组中符合条件的元素的索引。
const result = list.findIndex(function (item) {
return item.name === "李祥";
});
console.log(result);
(4)flat:用于合并数组。
const list = [1, 2, 3, [4, 5, 6, [ 7, 8]]];
const list2 = list.flat(2);
console.log(list2);
(5)filter:过滤出符合条件的元素,与find不同的是filter返回一个数组。
const result = list.filter(function (item) {
return item.name === "李祥";
});
console.log(result);
3.4.ES6数组中map方法
map用于将一个类型的数字转换成另外一个类型。
const list = [
{ name: "李祥", age: 15 },
{ name: "张三", age: 10 },
{ name: "李四", age: 19 },
{ name: "王武", age: 20 },
];
//遍历集合中每一项,创建obj对象,将原对象属性复制到新的obj中
//然后给obj增加一个属性state
const mapList = list.map(function (i) {
let obj = {};
//对象拷贝
Object.assign(obj, i);
obj.sex = 0;
return obj;
});
console.log(mapList);
3.5.ES6对象的新特性
创建对象
const a = {
name: 'lixiang',
age: 21,
address: {
city: 'BeiJing',
town: 'ChangYang'
},
};
(1)深拷贝简单对象
const b = { ...a };
console.log(b);
(2)给对象设置默认值
const b = { ...a, name: 'zhangsan' };
console.log(b);
(3)合并对象
const b = { sex:'0' };
const c = {...a,...b}
console.log(c);
(4)属性初始化、方法的简写
(5)Object新增的方法
- Object.is:判断两个对象是否相等
let res = Object.is({name:'lx'}, {name:'lx'});
console.log(res);
let res1 = Object.is(NaN, NaN);
console.log(res1);
- Object.assign:对象复制
let a = { name: 'lx', age: 24 };
let b = {};
let c = Object.assign(b, a);
console.log(c);
- Object.keys:获取对象所有的key
let a = { name: 'lx', age: 24 };
const arr = Object.keys(a);
console.log(arr);
- Object.values:获取对象所有的value
let a = { name: 'lx', age: 24 };
const arr = Object.values(a);
console.log(arr);
- Object.entries:获取key和value
let a = { name: 'lx', age: 24 };
const arr = Object.entries(a);
console.log(arr);
3.6.ES6新增Map与WeakMap
JavaScript中的对象,实质就是键值对的集合,但是在对象里却只能用字符串作为键名。在一些特殊的场景里就满足不了我们的需求了,正因为此,Map这一数据提出了,它是JavaScript中的一种更完善Hash结构。
Map对象:用于保存键值对,任何值(对象或者原始值)都可以作为一个键名或一个值。
- 属性及方法
属性/方法 | 作用 | 例子 |
---|---|---|
size | 返回键值对的数量 | m.size |
clear() | 清除所有键值对 | m.clear() |
has(key) | 判断键值对中是否有指定的键名,返回值是布尔值 | m.has(key) |
get(key) | 获取指定键名的键值,如不存在则返回undefined | m.get(key) |
set(key, value) | 添加键值对,如键名已存在,则更新键值对 | m.set(key, value) |
delete(key) | 删除指定键名的键值对 | m.delete(key) |
(1)创建一个Map
//创建一个空map
let map = new Map();
//创建一个带有默认值的map
let mapDef = new Map([
['name','lx'],
['age',12]
]);
console.log(map);
console.log(mapDef);
(2)API演示
//创建一个空map
let map = new Map();
//map中增加一个元素
map.set('name','lx');
map.set('age',20);
console.log(map);
//map中修改一个元素
//map中set方法如果有key则覆盖,没有则新增
map.set('name','zs');
console.log(map);
//返回键值对的数量
console.log(map.size);
//判断是否有某个key
console.log(map.has('name'));
//获取某个key的value
console.log(map.get('name'));
//删除某个key
map.delete('name');
console.log(map);
//清空
map.clear();
console.log(map);
(3)遍历器生成函数
const keys = map.keys();
const values = map.values();
const entries = map.entries();
console.log(Array.from(keys));
console.log(Array.from(values));
console.log(Array.from(entries));
WeakMap:只接受对象作为键名,而且无法遍历
const weakMap = new WeakMap([[{ name: 'lx' ,age: 21},{id:1,hobby:'唱、跳、rap、篮球'}]]);
console.log(weakMap);
3.7.ES6新增Set与WeakSet
Set是ES6给开发者提供的一种类似数组的数据结构,可以理解为值的集合。它和数组的最大的区别就在于: 它的值不会有重复项。
Set保证成员值唯一,用于集合去重。
- 属性及方法
属性/方法 | 作用 | 例子 |
---|---|---|
add | 新增元素 | s.add(key) |
size | 返回成员个数 | s.size |
clear() | 清除所有成员 | s.clear() |
has(value) | 判断键值对中是否有指定的值,返回值是布尔值 | s.has(key) |
delete(value) | 删除指定值 | s.delete(key) |
(1)创建一个Set
const a = { name: 'lx' };
const set = new Set([a]);
console.log(set);
const list = new Set([1, 2, 3, 4]);
console.log(list);
(2)API演示
const set = new Set([1, 2, 3, 4]);
console.log(set);
//获取set长度
console.log(set.size);
//判断是否存在2元素
console.log(set.has(2));
//删除2元素
set.delete(2);
console.log(set.has(2));
WeakSet:数组成员必须是对象
- WeakSet结构也提供了add( ) 方法,delete( ) 方法,has( )方法给开发者使用,作用与用法跟Set结构完全一致。
- WeakSet 结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys( ),values( ),entries( ),forEach( )等方法和size属性。
3.8.Array与Set的转换
// 数组转set
const list = [1, 2, 3];
const set = new Set(list);
console.log(set);
// set转数组
const arr = Array.from(set);
console.log(arr);
3.9.Object与Map的转换
// 对象转map
const obj = { nane: 'lx', age:24 };
const map = new Map(Object.entries(obj));
console.log(map);
// map转对象
const obj1 = Object.fromEntries(map);
console.log(obj1);
4.ES6新增高阶知识点
4.1.ES6中新增代理Proxy
(1)Proxy简介
正如Proxy的英译"代理"所示,Proxy是ES6为了操作对象引入的API。它不直接作用在对象上,而是作为一种媒介,如果需要操作对象的话,需要经过这个媒介的同意。
(2)使用方式
/* @params
** target: 用Proxy包装的目标对象
** handler: 一个对象,对代理对象进行拦截操作的函数,如set、get
*/
let p = new Proxy(target, handler)
(3)案例实战
const house = {
name: '三室一厅-次卧',
price: 1000,
phone: '18823139921',
id: '20230131',
state: '**',
};
const handle = {
get: function (obj, key) {
switch (key) {
case 'price':
return obj[key] + 500;
case 'phone':
return obj[key].substring(0, 3) + '****' + obj[key].substring(7);
default:
return obj[key];
}
},
set: function (obj, key, value) {
if (key === 'state') {
obj[key] = value;
}
},
};
const houseproxy = new Proxy(house, handle);
console.log(houseproxy.id);
console.log(houseproxy.phone);
console.log(houseproxy.price);
houseproxy.state='****';
console.log(houseproxy.state);
4.2.ES6中新增反射Refect
(1)Reflect简介
与Proxy相同,ES6引入Reflect也是用来操作对象的,它将对象里一些明显属于语言内部的方法移植到Reflect对象上,它对某些方法的返回结果进行了修改,使其更合理,更具语义化,并且使用函数的方式实现了Object的命令式操作。
- 属性及方法
属性/方法 | 作用 |
---|---|
Reflect.apply(target, thisArg, args) | 对一个函数进行调用操作,同时可以传入一个数组作为调用参数。 |
Reflect.construct(target, args) | 对构造函数进行 new 操作,相当于执行 new target(…args)。 |
Reflect.get(target,name, receiver) | 获取对象身上某个属性的值,类似于 target[name]。如果没有该属性,则返回undefined。 |
Reflect.set(target, name,value, receiver) | Reflect.defineProperty方法基本等同于Object.defineProperty,直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,不同的是,Object.defineProperty返回此对象。而Reflect.defineProperty会返回布尔值 |
Reflect.has(target,name) | 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。 |
Reflect.ownKeys(target) | 返回一个包含所有自身属性(不包含继承属性)的数组。(类似于 Object.keys(), 但不会受enumerable影响, Object.keys返回所有可枚举属性的字符串数组). |
Reflect.isExtensible(target) | 判断一个对象是否是可扩展的(是否可以在它上面添加新的属性),类似于 Object.isExtensible()。返回表示给定对象是否可扩展的一个Boolean 。(Object.seal 或 Object.freeze 方法都可以标记一个对象为不可扩展。) |
Reflect.preventExtensions(target) | 让一个对象变的不可扩展,也就是永远不能再添加新的属性。 |
4.3.Proxy实现数据双向绑定
(1)需求
实现一个双向的数据绑定。
(2)实现
- 编写html代码
<body>
请输入内容:<input type="text" id = "input"/><br/>
实时内容展示:<span id = "span"></span>
<script src="./js/index.js"></script>
</body>
- 编写js代码
// 获取dom节点
const input = document.getElementById("input");
const span = document.getElementById("span");
//定义双向绑定的对象
let obj = {};
//定义代理对象的handle方法
const handle = {
get: function (target, key) {
return target[key];
},
set: function (target, key, value) {
if (key === "text" && value) {
span.innerHTML = value;
return (target[key] = value);
}
},
};
//创建代理对象
const proxy = new Proxy(obj, handle);
//创建键盘的监听事件
input.addEventListener("keyup", function (e) {
proxy.text = e.target.value;
console.log(proxy.text);
});
5.函数的扩展、类及模块化
5.1.函数参数的扩展
(1)函数参数可设置默认值
//求和方法,当a和b都不传值时,默认都为1
function sum(a = 1, b = 1) {
return a + b;
}
console.log(sum());
console.log(sum(3));
console.log(sum(3,5));
(2)rest参数,可变参数
//求和方法,可变参数
function sum(...rest) {
let num = 0;
rest.forEach((e)=>{
num+=e;
})
return num;
}
console.log(sum(...[1,2]));
console.log(sum(...[1,2,4]));
5.2.ES6新增箭头函数
(1)箭头函数简介
ES6中允许使用=>来定义函数。箭头函数相当于[匿名函数,并简化了函数定义。
箭头函数在语法上比普通函数简洁多。箭头函数就是采用箭头=>来定义函数,省去关键字function。
无参:()=>{},等同于:function (){}
有参:(a,b)=>{},等同于:function (a,b){}
(2)箭头函数不绑定this
//箭头函数this指当前块级作用域
const obj = {
num: 10,
sum() {
setTimeout(() => {
console.log(this.num);
}, 1000);
},
};
obj.sum();
如果不是箭头函数,setTimeout()函数调用指的是当前window。
const obj = {
num: 10,
sum() {
setTimeout(function (){
console.log(this);
}, 1000);
},
};
obj.sum();
(3)什么情况下不能箭头函数
- 定义对象的方法
const obj = {
num: 10,
sum:() => {
setTimeout(() => {
console.log(this.num);
}, 1000);
},
};
obj.sum();
- 箭头函数没有arguments
const fun = () => {
console.log(arguments);
};
fun();
- 定义构造函数
const Car = () => {
console.log(this);
};
let car = new Car();
- 定义DOM事件的回调函数
const span = document.getElementById("span");
span.addEventListener("click", () => {
console.log(this);
this.innerHTML = "触发事件";
});
const span = document.getElementById("span");
span.addEventListener("click", function () {
console.log(this);
this.innerHTML = "触发事件";
});
5.3.ES6中新引入类的概念
ES6之前javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。
(1)定义类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`我是${this.name},今年${this.age}岁`);
}
}
const person = new Person("张三", 18);
person.sayHello();
console.log(person);
(2)类的继承,重写父类的方法
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
hello() {
console.log(`我是${this.name},今年${this.age}岁`);
}
}
class Student extends Person {
//子类重写父类属性
constructor(name, age, sex) {
super(name, age);
this.sex = sex;
}
hello() {
console.log(`我是${this.name},今年${this.age}岁,性别:${this.sex === '1'?'男':'女'}`);
}
}
const person = new Student('张三', 18,'1');
person.hello();
console.log(person);
(3)静态方法和静态属性
//静态方法和静态属性只能由类名进行调用
class Person {
static eyes = '眼睛';
static hello(){
console.log(`我是${this.name}`);
}
}
console.log(Person.eyes);
Person.hello();
5.4.ES6新增模块化开发
ES6之前是没有类的概念的,也就没有模块这一说法。理想情况下,开发者应该只注重核心业务的开发,对于其他有一定通用性的业务可以直接引入,也就是模块。多人开发,本应如此。
- CommonJS:Commonjs是作为Node中模块化规范以及原生模块面世的。
- AMD和RequireJs:AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后(前置依赖),这个回调函数才会运行。
Import 和 export案例
- export.js
const name = '李祥';
function hello(){
console.log('hello word');
}
class Car{
constructor(name){
this.name=name;
}
run(){
console.log(this.name+'跑起来了');
}
}
export default{
name,
hello,
Car,
}
- Import.js
import test from './export.js';
console.log(test.name);
test.hello();
- html中js引入要改成module类型
<script src="./js/import.js" type="module"></script>
6.ES6改进异步编程
6.1.Promise简介及案例
(1)什么是回调地狱
当异步操作想要有顺序时,只能在一个异步成功以后的回调函数里面嵌套另一个异步的操作,如果嵌套的层数过多就形成了回调地狱。
回调地狱的弊端:后期代码维护比较困难。
promise就是用来解决回调地狱。
promise封装了异步操作。创建一个promise对象,写一个函数作为参数,这个函数参数有两个参数,分别是resolve和reject。函数内部写异步操作,成功调resolve,失败调reject。这时promise对象就可以知道异步操作的状态。p.then(成功执行)p.catch(失败执行)。
promise本质就是一个状态机,记录异步内部操作的状态。
在es6之前写法代码耦合,出错时,无法排除。
function request() {
axios.get("a.json").then((res) => {
if (res && res.data.code === 0) {
console.log(res.data.data.data);
axios.get("b.json").then((res) => {
if (res && res.data.code === 0) {
console.log(res.data.data.data);
axios.get("c.json").then((res) => {
console.log(res.data.data.data);
});
}
});
}
});
}
request();
- 简单说Promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
- 从语法上说,Promise 是一个对象,从它可以获取异步操作的的最终状态(成功或失败)。
- Promise 是一个构造函数,提供统一的 API,Promise里不仅可以存放着异步的代码,也可以放同步的代码。
// 准备阶段
new Promise((resolve, reject) => {})
// 成功状态
new Promise((resolve, reject) => {
resolve('成功'); // 同步代码
}).then((res) => {
console.log(res);
});
// 失败状态
new Promise((resolve, reject) => {
reject('失败');
}).catch((err) => {
console.log(err);
});
-
Promise对象的状态不受外界影响
-
pending 初始状态
-
resolve 成功状态
-
rejected 失败状态
Promise 有以上三种状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态
-
-
Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 pending变成resolve或者由pending变成rejected
(2)Promise解决回调地狱案例
//先定义三个函数
function requestA() {
return new Promise((resolve, reject) => {
axios.get("a.json").then((res) => {
resolve(res);
});
});
}
function requestB() {
return new Promise((resolve, reject) => {
axios.get("b.json").then((res) => {
resolve(res);
});
});
}
function requestC() {
return new Promise((resolve, reject) => {
axios.get("c.json").then((res) => {
resolve(res);
});
});
}
function request() {
requestA().then((res) => {
console.log(res);
return requestB();
}).then((res) => {
console.log(res);
return requestC();
}).then((res) => {
console.log(res);
});
}
request();
(3)Promise.all方法
- Promise.all 实际上是一个函数,它接受一个
promise
数组并返回一个promise
。然后当所有的promise
都完成时会得到结果数组或者当其中一个被拒绝时会抛出错误。 - 返回值将会按照参数内的
promise
顺序排列,而不是由调用promise
的完成顺序决定。
function requestAll() {
Promise.all([requestA(), requestB(), requestC()])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
}
requestAll();
(4)Promise.race方法
Promse.race就是赛跑的意思,传入的promise
数组里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
function requestRace() {
Promise.race([requestA(), requestB(), requestC()])
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
}
requestRace();
6.2.优雅的异步编程async
(1)什么是async
async是异步的简写,Generator(生成器)的语法糖,用于声明一个函数是异步函数。
async function sum(a,b) {
await console.log(a+b);
}
sum(1,2);
async
写在函数前,返回一个Promise
对象。
async await改造promise.then异步调用。
async function request() {
try {
const a = await requestA();
const b = await requestB();
const c = await requestC();
console.log(a, b, c);
} catch (err) {
console.log(err);
}
}
request();