ES6教程
ES6 是 JavaScript 语言的下一代标准,使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言
ES6是有史以来最实质的升级,特性涵盖范围甚广,小到受欢迎的语法糖,例如箭头函数(arrow functions)和简单的字符串插值(string interpolation),大到烧脑的新概念,例如代理(proxies)和生成器(generators);它将彻底改变程序员们编写JS代码的方式。
ps:es6是基础走向进阶必须学的
变量声明let、const
代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。
// 代码块内,在声明变量 PI 之前使用它会报错。
var PI = "a";
if(true){
console.log(PI); // Cannot access 'PI' before initialization
const PI = "3.1415926";
}
var PI = "a";
if(true){
console.log(PI); // a
}
let
let 声明的变量只在 let 命令所在的代码块内有效,只能声明一次,可以重新赋值,重复声明会报错;不存在变量提升,在声明变量前调用,会报错!
for 循环计数器很适合用 let,使用var就需要立即执行函数,let不需要
console.log(a); //ReferenceError: a is not defined
let a = "apple";
console.log(b); //undefined
var b = "banana";
const
const 声明一个只读的常量,一旦声明,常量的值就不能改变。声明必须初始化,否则会报错
const 保证的不是该变量值不得改动,而是变量指向的内存地址所保存的数据不得改动。
对于简单类型的数据,值就保存在变量指向的那个内存地址,因此等同于常量;
对于复杂类型的数据,变量指向的内存地址,保存的只是一个指向实际数据的指针,const
只能保证这个指针是固定的,并不能确保改变量的结构不变
区别
6点区别:
变量提升、暂时性死区、块级作用域、重复声明、修改声明变量、使用
解构赋值
解构赋值是对赋值运算符的扩展。解构的源,解构赋值表达式的右边部分。解构的目标,解构赋值表达式的左边部分。
数组结构的解构赋值
1. 可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1 b = 2 c = 3
2. 可忽略
let [a, , b] = [1, 2, 3];
// a = 1 b = 3
3. 不完全解构
let [a = 1, b] = [];
// a = 1, b = undefined
4. 剩余运算符
let [a, ...b] = [1, 2, 3];
//a = 1 b = [2, 3]
5. 字符串等(解构的目标若为可遍历对象,皆可进行解构赋值)
let [a, b, c, d, e] = 'hello1231';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'
6. 解构默认值
当解构模式有匹配结果,且匹配结果是 undefined 时,会触发默认值作为返回结果。
let [a = 2] = [undefined]; // a = 2
let [a = 3, b = a] = []; // a = 3, b = 3
let [a = 3, b = a] = [1]; // a = 1, b = 1
let [a = 3, b = a] = [1, 2]; // a = 1, b = 2
对象模型的解构赋值
- 对象模型的解构赋值需要保持 键名统一
1. 可嵌套可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { }] } = obj;
// x = 'hello'
2. 不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'
3. 剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}
4. 解构默认值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;
Symbol
新的原始数据类型 Symbol ,表示独一无二的值,最大的用法是用来定义对象的唯一属性名。
- Symbol 函数栈不能用 new 命令,因为 Symbol 是原始数据类型,不是对象。
- Symbol 接受一个字符串作为参数,为新创建的 Symbol 提供描述
- 相同参数生成的 symbol 值不相等
Symbol 常作为属性名来使用,每一个 Symbol 的值都是不相等的,可以保证属性不重名
Symbol 作为对象属性名时不能用.运算符,要用方括号,因为.运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性。
let syObject = {};
syObject[sy] = "kk";
syObject[sy]; // "kk"
syObject.sy; // undefined
Symbol.for 方法可以检测上下文中是否已经存在使用该方法且相同参数创建的 symbol 值,如果存在则返回已经存在的值,如果不存在则新建。
const s1 = Symbol.for('foo');
const s2 = Symbol.for('foo');
console.log(s1 === s2); // true
需要注意的是:
Symbol 值作为属性名时,不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。
如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到。
使用场景
- 当一个复杂对象中含有多个属性的时候,很容易将某个属性名覆盖掉,利用 Symbol 值作为属性名可以很好的避免这一现象
- 模拟类的私有方法,ES6 中的类是没有 private 关键字来声明类的私有方法和私有变量的,但是我们可以利用 Symbol 的唯一性来模拟。
Map 与 Set
Map
Map 对象保存键值对,任何值(对象或者原始值) 都可以作为一个键或一个值。如果你需要“键值对”的数据结构,Map比Object更合适。
Maps 和 Objects 的区别
- 一个 Object 的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
- Map 中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。
- Map 的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算。
- Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
Map的构建
map的键可以是字符串、对象、函数、NaN
Map的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键,在扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
var myMap = new Map();
var keyObj = {},
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj); // "和键 keyObj 关联的值"
myMap.get({}); // undefined, 因为 keyObj !== {}
!!!虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),NaN作为Map的键来说是没有区别的。
var myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
var otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
作为构造函数,Map也可以接受一个数组作为参数。该数组的成员是一个个表示键值对的数组。
var map = new Map([
['name', '张三'],
['title', 'Author']
]);
map.size // 2
map.has('name') // true
map.get('name') // "张三"
//接受数组作为参数,实际上执行的算法
var map = new Map();
items.forEach(([key, value]) => map.set(key, value));
只有对同一个对象的引用,Map结构才将其视为同一个键
var map = new Map();
map.set(['a'], 555);
map.get(['a']) // undefined
// 表面是针对同一个键,但实际上这是两个值,内存地址是不一样的
Map 实例属性方法
- size属性
- set(key, value)
- get(key)
- **has(key)**,返回布尔值
- **delete(key)**,返回布尔值
- **clear()**,没有返回值
Map 遍历方法
for…of
对 Map 进行遍历键值对
for (var [key, value] of myMap) {
console.log(key + " = " + value);
}
/* 这个 entries 方法返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组。 */
for (var [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}
对 Map 进行遍历键对、值对
for (var key of myMap.keys()) {
console.log(key);
}
for (var value of myMap.values()) {
console.log(value);
}
结合数组方法
Map结构转为数组结构,比较快速的方法是结合使用扩展运算符(...
),结合数组的map
方法、filter
方法,可以实现Map的遍历和过滤(Map本身没有map
和filter
方法)。
let map0 = new Map()
.set(1, 'a')
.set(2, 'b')
.set(3, 'c');
let map1 = new Map(
[...map0].filter(([k, v]) => k < 3)
);
// 产生Map结构 {1 => 'a', 2 => 'b'}
forEach
Map还有一个forEach
方法,与数组的forEach
方法类似,也可以实现遍历。forEach方法还可以接受第二个参数,用来绑定this
var reporter = {
report: function(key, value) {
console.log("Key: %s, Value: %s", key, value);
}
};
map.forEach(function(value, key, map) {
this.report(key, value);
}, reporter);
Map对象的操作
Map和Array的转换
Map转为数组最方便的方法,就是使用扩展运算符(…),也可以使用 Array.from 函数
var kvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
var myMap = new Map(kvArray);
var outArray1 = Array.from(myMap);
var outArray2 = [...myMap];
Map 的克隆
var myMap1 = new Map([["key1", "value1"], ["key2", "value2"]]);
var myMap2 = new Map(myMap1);
console.log(original === clone);
// 打印 false。 Map 对象构造函数生成实例,迭代出新的对象。
Map 的合并
var first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],]); var second = new Map([[1, 'uno'], [2, 'dos']]); // 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的,对应值即 uno,dos, three var merged = new Map([...first, ...second]);
Set
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
set判断值唯一性,也会考虑值的类型,相当用===
常用于去除数组重复成员
1. 方法一
[...new Set([1, 2, 3, 4, 4])]
2. 方法二
var s = new Set();
[2, 3, 5, 4, 5, 2, 2].map(x => s.add(x));
Set属性方法
属性
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
方法
add(value)
:添加某个值,返回Set结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set
的成员。clear()
:清除所有成员,没有返回值。
遍历操作
keys()
,values()
,entries()
,forEach()
Set结构键名和键值是同一个值,所以key
方法和value
方法的行为完全一致
for (let x of new Set(['red', 'green', 'blue'])) {
console.log(x);
}
// red
// green
// blue
Set 中的特殊值
Set 对象存储的值总是唯一的,所以需要判断两个值是否恒等。有几个特殊值需要特殊对待:
- +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
- undefined 与 undefined 是恒等的,所以不重复;
- NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。
类型转换
// Array 转 Set new Set()
var mySet = new Set(["value1", "value2", "value3"]);
// Set 转 Array 用...操作符
var myArray = [...mySet];
// String 转 Set
var mySet = new Set('hello'); // Set(4) {"h", "e", "l", "o"}
weakset和weakmap
待补充
Proxy 与 Reflect
Proxy 与 Reflect 是 ES6 为了操作对象引入的 API
- Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
- Reflect 对象的方法与 Proxy 对象的方法是一一对应的。所以 Proxy 对象的方法可以通过调用 Reflect 对象的方法获取默认行为,然后进行额外操作。
作为构造函数,Proxy
接受两个参数。第一个参数是所要代理的目标对象(上例是一个空对象),即如果没有Proxy
的介入,操作原来要访问的就是这个对象;
第二个参数是一个配置对象,对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。
let target = {
name: 'Tom',
age: 24
}
let handler = {
get: function(target, key,receiver) {
if(key === 'age'){
return '年龄是保密的';
}
return Reflect.get(target, key,receiver);
}
}
let proxy = new Proxy(target, handler)
proxy.name //return Tom
proxy.age //return 年龄是保密的
Proxy 支持的拦截操作
- target,目标对象 ;propKey,即key;receiver, Proxy 实例本身
- ctx 表示目标对象上下文,args 表示目标对象的参数数组。
get
get(target, propKey, receiver)
set
set(target, propKey, value, receiver)
apply
apply(target, ctx, args)
has
has(target, propKey)
// 判断 target 对象是否存在 propKey 属性,此方法不判断一个属性是对象自身的属性,还是继承的属性。
deleteProperty(target, propKey)
function sub(a, b){
return a - b;
}
let handler = {
apply: function(target, ctx, args){
console.log('handle apply');
return Reflect.apply(...arguments);
}
}
let proxy = new Proxy(sub, handler)
proxy(2, 1) // handle apply 1
Reflect 的好处
- target[key] = value,没有返回值,Reflect 有返回值,可以看设置是否成功
- 原来set get 都挂载在Object上,不方便做统一管理
- Reflect 和 Proxy 使用方法上一一对应
字符串
子串的识别
let string = "apple,banana,orange";
//找在哪
string.indexOf('a') // 0
string.lastIndexOf('a') // 15
//找有没有
//以下三个方法都可以接受两个参数,需要搜索的字符串,和可选的搜索起始位置索引。
string.includes("banana"); // true
string.startsWith("apple"); // true
string.endsWith("apple"); // false
string.startsWith("banana",6) // true
字符串重复
repeat():返回新的字符串,表示将字符串重复指定次数返回。
如果参数是小数,向下取整;参数是 负数、infinity 会报错;参数是 NaN,等同于 repeat 零次;传入字符串,先将字符串转化为数字
字符串补全
padStart:返回新的字符串,表示用参数字符串从头部(左侧)补全原字符串。
padEnd:返回新的字符串,表示用参数字符串从尾部(右侧)补全原字符串。
- 参数:第一个参数是生成字符串最小长度,第二个参数是用来补全的字符串,若空用空格填充
- 如果指定的长度小于或者等于原字符串的长度,则返回原字符串
- 如果原字符串加上补全字符串长度大于指定长度,则截去超出位数的补全字符串
console.log("h".padStart(5,"o")); // "ooooh"
console.log("h".padEnd(5,"o")); // "hoooo"
console.log("h".padStart(5)); // " h"
console.log("hello".padStart(5,"A")); // "hello"
console.log("hello".padEnd(10,",world!")); // "hello,worl"
模板字符串
用反引号 ` 除了作为普通字符串,还可以用来定义多行字符串,还可以在字符串中通过 ${} 的方式加入变量、表达式、函数
模板字符串中的换行和空格都是会被保留的
function f(){
return "have fun!";
}
let string2= `Game start,${f()}`;
console.log(string2); // Game start,have fun!
标签模板
模板字符串可以紧跟在一个函数后面,该函数将被调用来处理这个模板字符串,这种称为“标签模板”功能。
该函数依次会接收到多个参数
- 所有静态文字都作为数组传递给第一个参数。
- 占位符表达式的所有值都作为其余参数传递。
- 变量替换只发生在数组的第一个成员与第二个成员之间、第二个成员与第三个成员之间,以此类推。
function tag(stringArr, value1, value2) {
// ...
}
//等同于
function tag(stringArr, ...values) {
// ...
}
数值
二进制表示法新写法: 前缀 0b 或 0B;八进制表示法新写法: 前缀 0o 或 0O
console.log(0b11 === 3); // true
console.log(0o11 === 9); // true
对象
对象字面量
// 属性的简洁表示法: ES6允许对象的属性直接写变量,这时候属性名是变量名,属性值是变量值
const age = 12;
const name = "Amy";
const person = {age, name};
person //{age: 12, name: "Amy"}
// 简写方法:
const person = {
sayHi(){
console.log("Hi");
}
}
person.sayHi(); //"Hi"
// 属性名表达式: 允许用表达式作为属性名,但要将表达式放在方括号内。
const obj = {
["he"+"llo"](){
return "Hi";
}
}
obj.hello(); //"Hi"
拓展运算符
拓展运算符(…)用于取出参数对象所有可遍历属性然后拷贝到当前对象,是一个非常常用的方法!
拓展运算符 后跟null、undefined、空对象,不会报错
// 基本用法
let person = {name: "Amy", age: 15}; let someone = { ...person }; someone; //{name: "Amy", age: 15}
// 可用于合并对象,合并时 属性名相同时,后面的覆盖前面的
let age = {age: 15};
let name = {name: "Amy"};
let person = {...age, ...name};
person; //{age: 15, name: "Amy"}
对象新方法
Object.assign(target, source_1, ···)
用法:将源对象的所有可枚举属性复制到目标对象中。
同名属性,遵循后面覆盖前面的原则
如果只有一个参数,当参数为对象时,直接返回该对象;当参数不是对象时,会先将参数转为对象然后返回。
- null 和 undefined 放在第一个参数时,不能转化为对象,会报错
- null 和 undefined 放在第二个参数或后面,会忽略而不会报错
Object.assign(3); // Number {3} typeof Object.assign(3); // "object" Object.assign(null); // TypeError: Cannot convert undefined or null to object Object.assign(1,undefined); // Number {1}
assign 的属性拷贝是浅拷贝,如果源对象某个属性值是对象,那么拷贝得到的是这个对象的引用
数组的处理
Object.assign([2,3], [5]); // [5,3]
//先将 [2,3] 转为 {0:2,1:3} ,然后再进行属性复制,所以源对象的 0 号属性覆盖了目标对象的 0。
Object.is(value1, value2)
用来比较两个值是否严格相等,与(===)基本类似
与(===)的两点区别:
//一是+0不等于-0
Object.is(+0,-0); //false
+0 === -0 //true
//二是NaN等于本身
Object.is(NaN,NaN); //true
NaN === NaN //false
数组
Array.of
方法用于将一组值,转换为数组
Array.from
方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象
// Array.of() 将参数中所有值作为元素形成数组。
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
console.log(Array.of(1, '2', true)); // [1, '2', true]
console.log(Array.of()); // []
// Array.from(arrayLike[, mapFn[, thisArg]]),将类数组对象或可迭代对象转化为数组
// mapFn 可选函数参数,用于对每个元素进行处理,放入数组的是处理后的元素
// thisArg 用于指定 map 函数执行时的 this 对象
console.log(Array.from([1, , 3])); // [1, undefined, 3]
let map = {
do: function(n) {
return n * 2;
}
}
let arrayLike = [1, 2, 3];
console.log(Array.from(arrayLike, function (n){
return this.do(n);
}, map)); // [2, 4, 6]
新增方法
- copyWithin()
- find()、findIndex()
- fill(),使用给定值,填充一个数组,第二、三个参数,指定填充的起始、结束位置
- entries(),keys(),values(),遍历键值对,键 为 index,值为 value
- includes()
- flat(),flatMap(),将数组扁平化处理,返回一个新数组,对原数据没有影响
函数
增加了默认参数,只有在未传递参数或undefined时,才使用默认参数,null被认为是有效的参数值
不定参数,用来表示不确定参数个数,形如,…变量名
function f(...values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
箭头函数
- 箭头函数严格模式下this也指向window
- 箭头函数 忽略任何形式的this指向改变,静态this指向
- 箭头函数 不是个 构造器,不能new
参数 => 函数体
1. 最基本写法:
var f = v => v;
2. 当箭头函数没有参数或者有多个参数,要用 () 括起来
var f = (a,b) => a+b;
3. 当函数体有多行语句,用 {} 包裹起来
var f = (a,b) => {
let result = a+b;
return result;
}
4. 当要返回对象的时候,要用 () 将对象包裹起来
var f = (id,name) => ({id: id, name: name});
- 箭头函数 没有 this、super、arguments 和 new.target 绑定
- this,指向window
- 使用arguments ,会报错
- 不可以作为构造函数,不能使用 new 命令,否则会报错
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象
class类
class 的本质是 function,可以看作一个语法糖,让对象原型的写法更加清晰,是构造函数的另一种写法
- class (类)作为对象的模板被引入,可以通过 class 关键字定义类;
- 类 不可以重复声明,类定义不会被提升
- 类里面的方法不加function关键字 方法与方法之间不用,号隔开
constructor关键字
- constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法
- 一个类必须有 constructor 方法,如果没有显式定义,一个空的 constructor 方法会被默认添加。类包含多个 constructor 的方法,则抛出 SyntaxError错误
class Person{
constructor (name) {
this.name = name //constructor内定义的方法和属性是实例对象自己的,
}
say () { //而constructor外定义的方法和属性则是所有实例对象可以共享的 注意!
console.log('hello')
}
}
console.log(typeof Person) //funciton
console.log(Person.prototype.constructor === Person) //true
let jon = new Person()
jon.hasOwnPrototype('name') //true
jon.hasOwnPrototype('say') //false
静态方法
不需要通过实例对象,可以直接通过类来调用的方法,其中的 this 指向类本身
- 静态方法可以被子类继承
- 静态方法可以通过类名调用,不能通过实例对象调用,否则会报错
class Person {
static doSay () {
this.say()
}
}
Person.doSay() //hello
//静态方法可以被子类继承
class Sub extends Person {
}
Sub.doSay() // hello
//静态方法可以通过类名调用,不能通过实例对象调用,否则会报错
class Person {
static sum(a, b) {
console.log(a + b)
}
}
var p = new Person()
Person.sum(1, 2) // 3
p.sum(1,2) // TypeError p.sum is not a function
setter 和 getter
class Person {
get name () {
return 'getter'
}
set name(val) {
console.log('setter' + val)
}
}
let jon = new Person()
jon.name = 'jon' // setter jon
jon.name // getter
super
super关键字用于访问和调用 父类上的函数,可以调用父类的构造函数 也可以调用父类的普通函数
- super在调用父类的构造函数时,会将父类构造函数的属性方法绑定到子类实例的构造对象中!super(),相当于执行了 new Father();
- super方法必须放在子类构造函数this调用之前,不然new Father()会覆盖掉this调用生成的对象
- super() 里面有参数,相当于给父类的构造函数传参数
extends
子类可以继承父类中的一些 方法和属性
模块
模块化,为了确定模块的依赖关系,以及输入和输出的变量。
ES6 的模块化分为导出(export) @与导入(import)两个模块。
特点
ES6 的模块自动开启严格模式
模块中可以导入和导出各种类型的变量,如函数,对象,字符串,数字,布尔值,类等。
每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域。
每一个模块只加载一次(是单例的), 若再去加载同目录下同文件,直接从内存中读取。
export、import、as
- export 命令可以出现在模块的任何位置,但必需处于模块顶层。
- import 命令会提升到整个模块的头部,首先执行。
- as 对于模板内部变量重命名,可以导入重命名 也可以导出重命名
/*-----export [test.js]-----*/
let myName = "Tom";
export { myName as exportName }
/*-----import [xxx.js]-----*/
import { exportName } from "./test.js";
console.log(exportName);// Tom
import
只读属性:可以改写 import 变量类型为对象的属性值,不能改写 import 中基本类型的值。
单例模式:多次重复执行同一句 import 语句,那么只会执行一次
静态执行特性:import 是静态执行,所以不能使用表达式和变量。
import { "f" + "oo" } from "methods"; // error
export default 命令
- 在一个文件或模块中,export、import 可以有多个,export default 仅有一个。
- export default 中的 default 是对应的导出接口变量。
- 通过 export 方式导出,在导入时要加{ },export default 则不需要。
- export default 向外暴露的成员,可以使用任意变量来接收。
var a = "My name is Tom!";
export default a; // 仅有一个
export default var c = "error";
// error,default 已经是对应的导出变量,不能跟着变量声明语句
import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收
Promise对象
Promise是解决异步流程化的一种手段,从语法上说,Promise 是一个对象
Promise 有三种状态:pending(进行中)、resolved(已成功)和 rejected(已失败)
- Promise 对象只有:从 pending 变为 fulfilled 和从 pending 变为 rejected 的状态改变
Promise 是构造函数,需要new,只有一个函数参数 excutor执行器,执行器有两个参数,resolve接收成功,reject接收失败
promise接受then方法调用,接受两个函数参数,第一个函数接收resolve的参数,第二个参数接收reject参数
excutor是同步执行,then是异步调用,在 JavaScript 事件队列的当前运行完成之前,then回调函数永远不会被调用
通过 .then 形式添加的回调函数,是可以链式调用的,中间需要return出去
浏览器中不能终止的 Promise 链里的 rejection,建议后面都跟上 .catch(error => console.log(error));
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一个then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二个then // 2
console.log(value);
}).then(function(value){ // 第三个then // undefined
console.log(value);
});
Generator 函数
Generator 函数,可以通过 yield 关键字,把函数的执行流挂起,为改变执行流程提供了可能,从而为异步编程提供解决方案。
Generator 有两个区分于普通函数的部分:
- 函数名之前有个 * ; * 用来表示函数为 Generator 函数
- 函数内部有 yield 表达式,用来定义函数内部的状态
Generator 函数不会像普通函数一样立即执行,而是返回一个指向内部状态对象的指针,所以要调用遍历器对象Iterator 的 next 方法,指针就会从函数头部或者上一次停下来的地方开始执行。
function* func(){
console.log("one");
yield '1';
console.log("two");
yield '2';
console.log("three");
return '3';
}
f = func();
f.next();
// one
// {value: "1", done: false}
f.next();
// two
// {value: "2", done: false}
f.next();
// three
// {value: "3", done: true}
f.next();
// {value: undefined, done: true}
async 函数
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数
async function helloAsync(){
return "helloAsync";
}
console.log(helloAsync()) // Promise {<resolved>: "helloAsync"}
helloAsync().then(v=>{
console.log(v); // helloAsync
})
await
await 操作符用于等待一个 Promise 对象,async 函数中遇到 await 就会先暂停执行,等到触发 操作完成后,恢复 async 函数的执行并返回解析值
await 关键字仅在 async function 中有效。如果在 async function 函数体外使用 await ,你只会得到一个语法错误