ES6
https://es6.ruanyifeng.com/
ES 全称 EcmaScript , 是脚本语言的规范 而平时经常编写的 JavaScript,是 EcmaScript 的一种实现 ,所以 ES 新特性就是 JavaScript 新特性
let 声明变量
let 局部变量
var 全局变量
let : 块级作用域 不存在变量提升 变量不能重复声明
const 声明常量
变量名一般大小
数组声明
// 数组结构
const arr = [1,2,3,4,5];
let [a,b,c,d,e] = arr;
console.log(a,b,c,d,e); // 1 2 3 4 5
对象声明
// 对象声明
const obj = {
name: '张三',
age: 18,
fun: function(){
console.log('我是一个函数');
}
};
let {name,age,fun} = obj;
console.log(name,age,fun); // 张三 18 function
fun(); // 我是一个函数
模板字符串
// 声明字符串 (模板字符串) ``(ES6 新增) '' ""
const str = `我是一个模板字符串`;
console.log(str); // 我是一个模板字符串
可以直接出现换号符 不用拼接
// 可以直接出现换号符
let set = `<ul>
<li>嘿嘿</li>
<li>嘿嘿</li>
<li>嘿嘿</li>
</ul>`;
变量拼接 ${}
// 变量拼接
let str1 = '我是一个变量';
let str2 = `${str1}hihihihihihihih`;
console.log(str2); // 我是一个变量hihihihihihihih
ES6 允许在大括号里面, 写入变量和函数,作为对象的属性和方法
let aaa = '我是一个变量';
let obj1 = function(){
console.log('我是一个对象');
}
let bbb = {
aaa,
obj1,
ccc(){
console.log('我是一个方法');
}
}
console.log(bbb); // { aaa: '我是一个变量', obj1: function, ccc: function }
箭头函数
let fun1 = () => {
console.log('我是一个箭头函数');
}
fun1(); // 我是一个箭头函数
-
this 是静态的 this始终指向函数声明时所在作用域 下的 this 的值
-
不能作为构造实例化对象
-
不能使用 arguments 变量
简写
- 省略小括号,当形参有且只有一个的时候
let fun2 = a => console.log(a);
fun2('嘿嘿'); // 嘿嘿
- 省略花括号 当代码只有一条语句时 ,此时 return 必须省略
let fun3 = (a,b) => a+b;
fun3(1,2); // 3
函数参数的默认值设置
let fun4 = (a,b=10) => a+b;
console.log(fun4(1)); // 11
rest 参数
// rest 参数 用于获取函数的多余参数 (ES6 新增) 用于代替arguments
let fun5 = (...args) => {
console.log(args); // [1,2,3,4,5]
}
fun5(1,2,3,4,5);
// ES5
function fun6(){
console.log(arguments);
}
fun6(1,2,3);
rest 参数必须要放在最后
...
号把数组转换成参数 扩展运算符
// ... 号把数组转换成参数 扩展运算符
let fun7 = (...args) => {
console.log(args); // [1,2,3,4,5]
}
fun7(1,2,3,4,5);
// 输出
0: 1
1: 2
2: 3
3: 4
4: 5
let fun8 = (args) => {
console.log(args); // [1,2,3,4,5]
}
fun8(1,2,3,4,5);
// 输出 1
数组合并
// 数组合并
let arr1 = [1,2,3];
let arr2 = [4,5,6];
let arr3 = [...arr1,...arr2];
console.log(arr3); // [1,2,3,4,5,6]
数组克隆
// 数组克隆
let arr4 = [...arr1];
console.log(arr4); // [1,2,3]
Symbol
ES6 引入一种新的数据类型 Symbol ,表示独一无二的值,它是 javaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用 for…in 循环遍历,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
// 创建 Symbol
let s = Symbol();
console.log(s, typeof s); // Symbol() 'symbol'
let s1 = Symbol('小涛');
let s2 = Symbol('小涛');
console.log(s1,s2); // Symbol(小涛) Symbol(小涛)
console.log(s1 === s2); // false
let s3 = Symbol.for('小涛');
let s4 = Symbol.for('小涛');
console.log(s3,s4); // Symbol(小涛) Symbol(小涛)
console.log(s3 === s4); // true
Symbol 使用
// Symbol 使用
// 可以很安全的向对象中添加值 而不会影响原来的对象
let game = {
name: '小涛',
up: function(){
console.log('up');
},
down: function(){
console.log('down');
}
};
let methods = {
up: Symbol(),
down: Symbol()
};
game[methods.up] = function(){
console.log('up');
}
game[methods.down] = function(){
console.log('down');
}
console.log(game);
// {name: '小涛', up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
Symbol 内置值
Symbol.hasInstance | 对象的Symbol.hasInstance 属性,指向一个内部方法。当其他对象使用instanceof 运算符,判断是否为该对象的实例时,会调用这个方法。比如,foo instanceof Foo 在语言内部,实际调用的是Foo[Symbol.hasInstance](foo) |
---|---|
Symbol.isConcatSpreadable | 对象的Symbol.isConcatSpreadable 属性等于一个布尔值,表示该对象用于Array.prototype.concat() 时,是否可以展开。 |
Symbol.isConcatSpreadable | 属性也可以定义在类里面。 |
Symbol.species | 对象的Symbol.species 属性,指向一个构造函数。创建衍生对象时,会使用该属性。 |
Symbol.match | 对象的Symbol.match 属性,指向一个函数。当执行str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 对象的Symbol.replace 属性,指向一个方法,当该对象被String.prototype.replace 方法调用时,会返回该方法的返回值。 |
Symbol.search | 对象的Symbol.search 属性,指向一个方法,当该对象被String.prototype.search 方法调用时,会返回该方法的返回值。 |
Symbol.split | 对象的Symbol.split 属性,指向一个方法,当该对象被String.prototype.split 方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象的Symbol.iterator 属性,指向该对象的默认遍历器方法。 |
Symbol.toPrimitive | 对象的Symbol.toPrimitive 属性,指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol.toStringTag | 对象的Symbol.toStringTag 属性,指向一个方法。在该对象上面调用Object.prototype.toString 方法时,如果这个属性存在,它的返回值会出现在toString 方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object] 或[object Array] 中object 后面的那个字符串。 |
Symbol.unscopables | 对象的Symbol.unscopables 属性,指向一个对象。该对象指定了使用with 关键字时,哪些属性会被with 环境排除。 |
7种数据类型
USONB you are so niubility
u undefined
s string symbol
o object
n null number
b boolean
迭代器
作用:
- 为各种数据结构,提供一个统一的、简便的访问接口;
- 使得数据结构的成员能够按某种次序排列;
- ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
消费。 - 原生具备 iterator 接口的数据 (可用 for…of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
for…in
// for...of 循环
let arr5 = ['小涛','小明','小红'];
// of : 保留的是值
for(let i of arr5){
console.log(i); // 小涛 小明 小红
}
// in 保留的是键
for(let i in arr5){
console.log(i); // 0 1 2
}
生成器
是一个特殊的函数 异步编程
yield 可以暂停函数的执行 并且返回一个值 下次调用时 从上次暂停的地方继续执行 如果没有暂停 则从头执行
function* generator(){
console.log('111');
yield 1;
console.log('222');
yield 2;
console.log('333');
yield 3;
console.log('444');
}
let iterator = generator();
iterator.next(); // 111
iterator.next(); // 222
iterator.next(); // 333
iterator.next(); // 444
for(let i of generator()){
console.log(i); // 1 2 3
}
1s 控制台输出 111 2s 控制台输出 222 3s 控制台输出 333
function one(){
setTimeout(() => {
console.log('111');
iterator.next();
},1000)
}
function two(){
setTimeout(() => {
console.log('222');
iterator.next();
},2000)
}
function three(){
setTimeout(() => {
console.log('333');
iterator.next();
},3000)
}
function * gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
🍎 Promise
Promise 是 ES6 引入的异步编程解决方案 是一个构造函数 用来封装异步操作并可用获取其成功或失败的结果
Promise 异步实例
// 实例化 Promise 对象
const p = new Promise((resolve,reject) => {
setTimeout(function(){
let num = Math.random();
if(num > 0.5){
let data = '数据库中获取的数据';
resolve(data);
}else{
reject('数据读取失败');
}
},1000);
});
// 调用
p.then((data) => {
console.log(data);
}).catch((err) => {
console.log(err);
});
文件读取
// 引入 fs 模块
const fs = require('fs');
// 调用方法读取文件
const p = new Promise((resolve, reject) => {
fs.readFile('../resources/前端.md', 'utf8', (err, data) => {
if(err) reject(err); // 读取失败
resolve(data); // 读取成功
});
});
p.then(function(data) {
console.log(data.toString());
}).catch(function(err) {
console.log(err);
});
Ajax 封装
// 原生ajax
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'https://api.github.com/users/github');
// 发送
xhr.send(null);
// 监听状态改变
xhr.onreadystatechange = function() {
// 判断
if (xhr.readyState === 4) {
// 判断状态码
if (xhr.status >= 200 && xhr.status < 300) {
// 获取数据
const data = JSON.parse(xhr.responseText);
console.log(data);
} else {
console.log('error');
}
}
}// 原生ajax
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'https://api.github.com/users/github');
// 发送
xhr.send(null);
// 监听状态改变
xhr.onreadystatechange = function() {
// 判断
if (xhr.readyState === 4) {
// 判断状态码
if (xhr.status >= 200 && xhr.status < 300) {
// 获取数据
const data = JSON.parse(xhr.responseText);
console.log(data);
} else {
console.log('error');
}
}
}
// Promise封装ajax
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.github.com/users/github');
xhr.send(null);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
const data = JSON.parse(xhr.responseText);
resolve(data);
} else {
reject(xhr.statusText);
}
}
}
})
// 回调函数
p.then(data => console.log(data), err => console.log(err));
then 方法
then 方法返回的结果是 Promise 对象, 对象状态由调用函数的执行结果决定
读取多个文件
// 引入 fs 模块
const fs = require('fs');
// 引入 Promise 模块
const p = new Promise((resolve, reject) => {
fs.readFile('../resources/1.md', 'utf8', (err, data) => resolve(data));
});
p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile('../resources/2.md', 'utf8', (err, data) => resolve([value, data]));
});
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile('../resources/3.md', 'utf8', (err, data) => {
value.push(data);
resolve(value);
});
});
}).then(value => {
console.log(value.join('\r\n'));
}).catch(err => {
console.log(err);
});
catch() 方法
失败回调
Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set 结构的实例有以下属性。
Set.prototype.constructor
:构造函数,默认就是Set
函数。Set.prototype.size
:返回Set
实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。
Set.prototype.add(value)
:添加某个值,返回 Set 结构本身。Set.prototype.delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set
的成员。Set.prototype.clear()
:清除所有成员,没有返回值。
// set
let s = new Set();
s.add(1);
s.add(2);
s.add(3);
s.add(2);
console.log(s, typeof s);
// s.delete(2); // 删除指定的元素
console.log(s.has(1)); // true
console.log(s.size); // 3
s.clear(); // 清空集合
// 数组去重
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 5 ,3, 5, 7, 3 ,2];
let result = [...new Set(arr)];
console.log(result);
// 交集
let s1 = new Set([1, 2, 3]);
let s2 = new Set([2, 3, 4]);
let s3 = new Set([...s1].filter(x => s2.has(x)));
console.log(s3);
// 并集
let s4 = new Set([...s1, ...s2]);
console.log(s4);
// 差集
let s5 = new Set([...s1].filter(x => !s2.has(x)));
console.log(s5);
Map
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
const m = new Map();
const o = {p: 'Hello World'};
m.set(o, 'content')
m.get(o) // "content"
m.has(o) // true
m.delete(o) // true
m.has(o) // false
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。
class
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。
基本上,ES6 的class
可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
class Person {
// 构造函数 名字不能修改
constructor(name, age) {
this.name = name;
this.age = age;
}
// 实例方法 必须使用该语法
call() {
console.log('嘿嘿嘿嘿');
}
}
let p = new Person('张三', 18);
console.log(p);
类的静态成员
// class
class Person {
static say() {
console.log('hello');
}
constructor(name) {
this.name = name;
}
}
let p = new Person('张三');
console.log(p.name); // 张三
console.log(Person.say()); // hello
console.log(p.say()); // undefined
静态成员只能有 原生对象调用 不能用实例化对象调用
继承
// 继承
class Student extends Person {
constructor(name, age, school) {
super(name, age);
this.school = school;
}
call() {
console.log('嘿嘿嘿嘿');
}
}
let sut = new Student('李四', 18, '清华');
console.log(sut.name); // 李四
console.log(sut.school); // 清华
console.log(sut.call()); // undefined
console.log(sut.say()); // undefined
重写
// 重写
class Animal {
constructor(name) {
this.name = name;
}
say() {
console.log('喵喵喵');
}
}
let a = new Animal('小猫');
console.log(a.name); // 小猫
console.log(a.say()); // 喵喵喵
class Cat extends Animal {
say() {
console.log('汪汪汪');
}
}
let c = new Cat('小狗');
console.log(c.name); // 小狗
console.log(c.say()); // 汪汪汪
get set
// get set
class Person2 {
constructor(name) {
this.name = name;
}
get name() {
console.log('获取值');
return this._name;
}
set name(value) {
console.log('设置值');
this._name = value;
}
}
let p2 = new Person2('张三');
console.log(p2.name); // 张三
p2.name = '李四';
console.log(p2.name); // 李四
数值扩展
// js 的 最小精度
Number.EPSILON; // 精度
// 检测数据是否有限
Number.isFinite(1); // 是否是有限的
Number.isFinite(NaN); // false
Number.isFinite(Infinity); // false
Number.isInteger(1); // 是否是整数
Number.isInteger(1.1); // false
Number.parseInt('12'); // 转换成整数
Number.parseInt('12.1'); // 12
Number.parseFloat('12.1'); // 转换成浮点数
Number.parseFloat('12'); // 12
Math.floor(1.1); // 向下取整
Math.ceil(1.1); // 向上取整
Math.round(1.1); // 四舍五入
Math.trunc(1.1); // 去除小数部分
// 判断一个是否是 正数 负数 还是零
Math.sign(1.1); // 1
Math.sign(-1.1); // -1
Math.sign(0); // 0
// 二进制
0b11111111; // 255
0b11111111111111111111111111111111; // 18446744073709551615
// 八进制
0o377; // 255
0o77777777; // 18446744073709551615
// 十六进制
0xFF; // 255
0xFFFFFFFF; // 4294967295
对象扩展
Object.assign()
// 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
const obj = { a: 1, b: 4 };
const copy = Object.assign({
b: 2,
c: 3
}, obj);
console.log(copy); // { a: 1, b: 4, c: 3 }
Object.is()
// 方法用来比较两个值是否相等,与严格相等运算符(===)的行为基本一致
console.log(Object.is(1, 1)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(+0, -0)); // false
Object.setPrototypeOf()
// 方法用于设置一个对象的 prototype 属性
const obj2 = {};
Object.setPrototypeOf(obj2, Array.prototype);
console.log(obj2.push(1)); // 1
模块化
是指将一个大的程序文件 拆分成许多个小文件 然后将小文件组合起来
优点:
- 防止代码冲突
- 代码复用
- 高维护性
模块功能主要两个命令构成: export 和 import
- export 命令用于规范模块的对外接口
- import 命令用于输入其他模块提供的功能
暴露
单独暴露
// export 暴露
export let shool = '清华';
export function say() {
console.log('hello');
}
<script type="module">
// 引入 模块化.js 文件
import * as m1 from './js/模块化.js';
console.log(m1);
</script>
整体暴露
let shool = '清华';
function say() {
console.log('hello');
}
// export 暴露
export {shool, say};
默认暴露
// export 默认暴露
export default{
shool: '清华',
say: function () {
console.log('hello');
}
};
引入文件
// 通用
import * as m1 from './js/m1.js';
console.log(m1);
// 解构赋值形式
import {shool, say} from './js/m2.js';
// 名称相同需要重命名
import {shool as school} from './js/m3.js';
console.log(shool, say);
// 简便形式 针对默认暴露
import m3 from './js/m3.js';
方式二
// 模块引入
import * as m1 from './m1.js';
import * as m2 from './m2.js';
console.log(m1.shool);
console.log(m2.say);
<script src="./js/app.js" type="module"></script>
Babel
js 编译器 下一代 js 语法
能够把ES6以上的语法 转化成 ES5 使用低端浏览器支持
a.js
export let a = 1;
export function b() {
return a;
}
b.js
let school = '清华大学';
function getSchool() {
return school;
}
export {school, getSchool};
c.js
export default {
school: '清华大学',
getSchool: function() {
return this.school;
}
};
app.js
import * as m1 from './a.js';
import {school, getSchool} from './b.js';
import m3 from './c.js';
console.log(m1.a);
console.log(m1.b());
console.log(school);
console.log(getSchool());
console.log(m3.school);
console.log(m3.getSchool());
index.html
<script src="./src/js/app.js" type="module"></script>
安装工具
babel-cli
babel-preset-env
browserify
npm init --yes
npm i babel-cli babel-preset-env browserify -D
转化 (将 src/js 下的文件 转化到 dist/js 文件下)
npx babel src/js -d dist/js --presets=babel-preset-env
打包
npx browserify dist/js/app.js -o dist/bundle.js
导入 jquery
npm i jquery
// 修改背景颜色
import $ from 'jquery'; // 引入jquery
$('body').css('background-color', '#ccc');
重新执行
npx babel src/js -d dist/js --presets=babel-preset-env
npx browserify dist/js/app.js -o dist/bundle.js
ES7
// includes 判断是否包含
const mizi = ['小灰灰','小黑黑','小辉辉'];
console.log(mizi.includes('小灰灰'));
console.log(mizi.includes('小黑'));
// 扩展运算符 ** 幂运算
console.log(2 ** 10);
ES8
async 和 await
async 和 await 结合可以让异步代码像同步代码一样
async 函数
- async 函数的返回值为 promise 对象
// async
// 返回的结果不是一个 Promise 类型的对象,返回的结果就是成功的 Promise 对象
// 抛出错误 返回的是一个失败的promise
async function getData() {
return 'hello';
}
const result = getData();
console.log(result);
// Promise {<fulfilled>: 'hello'}
// 返回的结果是一个 Promise 对象,如果返回成功的是数据 则返回成功的 Promsie 对象 失败的数据 返回失败的 PRomise 对象
async function fu(){
return new Promise((resolve,reject) => {
setTimeout(() => {
// resolve('hello');
reject('error');
},1000);
})
}
c = async () => {
const data = await fu();
console.log(data);
}
- promise 对象的结果由 async 函数执行的返回值决定
await 表达式
- await 必须写在 async 函数中
- await 右侧的表达式一般为 promise 对象
- await 返回的是 promise 成功的值
- await 的 promise 失败了 就会抛出异常 需要通过 try…catch 捕获处理
// 创建 promise 对象
const p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve('hello');
// reject('error');
},1000);
});
// await 要放在 async 函数里面
async function f() {
try {
const data = await p;
console.log(data); // resolve
} catch (e) {
console.log(e); // reject
}
}
// 调用
f();
async和await结合读取文件
// 引入 fs 模块
const fs = require('fs');
// 读取文件
function readFile(fileName) {
return new Promise((resolve, reject) => {
fs.readFile(fileName, (err, data) => {
if (err) reject(err); // 如果有错误,则抛出错误
resolve(data); // 如果没有错误,则返回数据
});
});
}
// 声明一个 async 函数
async function main() {
try {
// 调用 readFileOne 函数,并将返回的结果保存在变量中
let md1 = await readFile('../resources/1.md');
let md2 = await readFile('../resources/2.md');
let md3 = await readFile('../resources/3.md');
// 输出结果
console.log(md1.toString());
console.log(md2.toString());
console.log(md3.toString());
} catch (err) {
console.log('文件读取异常: ', err);
}
}
main();
async和await封装AJAX请求
function sendAJAX(url) {
return new Promise(function(resolve,reject) {
// 创建对象
const xhr = new XMLHttpRequest();
// 设置请求方法和请求地址
xhr.open('GET', url);
// 设置发送的数据的格式
xhr.setRequestHeader('Content-Type', 'application/json');
// 发送
xhr.send();
// 注册响应函数
xhr.onreadystatechange = function() {
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response); // 成功
}else {
reject(new Error(xhr.statusText)); // 失败
}
}
}
})
}
// promise then 方法测试
sendAJAX('https://api.github.com/users/github').then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// async 和 await 测试
async function main() {
try {
let result = await sendAJAX('https://api.github.com/users/github');
console.log(result);
} catch {
console.log('error');
}
}
main();
对象方法扩展
// 声明对象
const school = {
name: '清华大学',
cities: ['北京', '上海', '广州'],
xueke: ['数学', '英语', '物理']
};
// 获取对象所有的键
const keys = Object.keys(school);
console.log(keys);
// 获取对象所有的值
const values = Object.values(school);
console.log(values);
// 获取对象所有的键值对
const entries = Object.entries(school);
console.log(entries);
// 创建 Map
const map = new Map(entries);
console.log(map);
// 对象属性的描述对象
console.log(Object.getOwnPropertyDescriptors(school));
ES9
Rest 参数与 spread 扩展运算符在 ES6 中已经引入 不过 ES6 只针对数组 在 ES9 中为对象提供了想数组一样的 rest 参数和扩展运算符
function connect({host, port, ...rest}){
console.log(host,port,rest);
}
connect({
host:'localhost',
port:8080,
db:'test',
user:'root'
});
// localhost 8080 {db: 'test', user: 'root'}
正则表达式
命名捕获分组
// 正则表达式
let str = '<a href="https://www.xiaotao.cloud">小涛</a>';
let reg = /<a href="(.*)">(.*)<\/a>/;
let result1 = str.match(reg);
console.log(result1);
// 命名捕获分组
let str2 = '<a href="https://www.xiaotao.cloud">小涛</a>';
let reg2 = /<a href="(?<url>.*)">(?<name>.*)<\/a>/;
let result2 = str2.match(reg2);
console.log(result2);
console.log(result2.groups); // {url: "https://www.xiaotao.cloud", name: "小涛"}
console.log(result2.groups.url); // https://www.xiaotao.cloud
dotAll 模式
匹配所有字符 不包括换行符