ES简介

ES全称EcmaScript,是脚本语言的规范,而平时编写的JavaScript是EcmaScript的一种实现,所以ES新特性其实质就是JavaScript的新特性


ES6

let关键字

//声明变量
let a;
let b, c, d;
let e = 100;
let f = 521, g = 'iloveyou', h = [];

let 关键字用来声明变量

  • 不允许重复声明
let star = '罗志祥'; // 重复声明变量,变量被污染
let star = '小猪'; // 重复声明会报错,var可以重复声明
  • 块儿级作用域 全局, 函数, eval(es5的严格模式)
// if else while for 
{
let girl = '周扬青';
var boy = '周扬';
}
console.log(girl); // 报错
console.log(boy); // 周扬
  • 不存在变量提升
console.log(song); // undefined
var song = '恋爱达人';

console.log(song); // 报错
let song = '恋爱达人';
  • 不影响作用域链
{
let school = '尚硅谷';

function fn() {
console.log(school); // 尚硅谷
}

fn();
}

实践案例

  • 三个div点击其中的一个使其颜色变成粉色
//获取div元素对象
let items = document.getElementsByClassName('item');

//遍历并绑定事件
for (let i = 0; i < items.length; i++) {
items[i].onclick = function () {
//修改当前元素的背景颜色
// this.style.background = 'pink';
items[i].style.background = 'pink';
}
}

//遍历并绑定事件
for (var i = 0; i < items.length; i++) {
items[i].onclick = function () {
//修改当前元素的背景颜色
this.style.background = 'pink';
//items[i].style.background = 'pink'; // items[3] 报错
}
}
// 循环体执行完i的值
console.log(window.i) // 3

const

const声明常量

  • 声明必须赋予初始值
const A; // 报错
  • 标识符一般为大写
const a = 100; // 不报错(潜规则)
  • 值不允许修改
const SCHOOL = '尚硅谷';
SCHOOL = 'ATGUIGU'; // 报错
  • 块儿级作用域
{
const PLAYER = 'UZI';
}
console.log(PLAYER); // 报错
  • 不允许重复声明
 const SCHOOL = '尚硅谷';
const SCHOOL = 'ATGUIGU'; // 报错

注意: 对象属性修改和数组元素变化不会出发 const 错误

const TEAM = ['UZI', 'MXLG', 'Ming', 'Letme'];
TEAM.push('Meiko'); // 常量所指向的地址没变,所以不报错
TEAM = 100; // 报错

应用场景:声明对象类型使用 const,非对象类型声明选择 let


变量的解构赋值

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

数组的解构

const F4 = ['小沈阳', '刘能', '赵四', '宋小宝'];
let [xiao, liu, zhao, song] = F4;
console.log(xiao); // 小沈阳
console.log(song); // 宋小宝

对象的解构

const zhao = {
name: '赵本山',
age: '不详',
xiaopin: function () {
console.log("我可以演小品");
}
};
let { name, age, xiaopin } = zhao;
console.log(name); // 赵本山
xiaopin(); // 我可以演小品

注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式


模板字符串

模板字符串(template string): 是增强版的字符串,用反引号(`)标识
ES6 引入新的声明字符串的方式 ```` ‘’ “”

字符串中可以出现换行符

let str = `<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`;
// 引号加号
let str = '<ul>' +
'<li>' + 沈腾 + '</li>' +
'<li>' + 玛丽 + '</li>' +
'</ul>'

可以使用 ${xxx} 形式输出变量

let lovest = '魏翔';
// let out = lovest + '是我心目中最搞笑的演员!!';
let out = `${lovest}是我心目中最搞笑的演员!!`;

注意:当遇到字符串与变量拼接的情况使用模板字符串


简化对象写法

ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法

let name = '尚硅谷';
let change = function () {
console.log('我们可以改变你!!');
}

const school = {
name,
change,
improve() {
console.log("我们可以提高你的技能");
}
}

console.log(school);

箭头函数

「箭头」(=>)定义函数

// 声明函数
let fn = function () {

}
let fn = (a, b) => {
return a + b;
}
//调用函数
let result = fn(1, 2);
console.log(result);
  • 箭头函数 this 指向函数声明时所在作用域下 this 的值,this是静态的
function getName() {
console.log(this.name);
}

let getName2 = () => {
// 函数声明时所在作用域下 this 的值
console.log(this.name);
}

//设置 window 对象的 name 属性
window.name = '尚硅谷';
const school = {
name: "ATGUIGU"
}

//直接调用
getName(); // 尚硅谷
getName2(); // 尚硅谷

//call 方法调用,改变函数内部this值
getName.call(school); // ATGUIGU
getName2.call(school); // 尚硅谷
  • 箭头函数不能作为构造函数实例化
let Person = (name, age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao', 30); // 报错
  • 不能使用 arguments(保存实参) 变量
let fn = () => {
console.log(arguments); // 报错,arguments未定义
}
fn(1, 2, 3);
  • 箭头函数的简写
      1. 省略小括号, 当形参有且只有一个的时候
      1. 省略花括号, 当代码体只有一条语句的时候, 花括号可以省略,return 必须省略,函数的返回值为该条语句的执行结果
let fn2 = num => {
return num * 10;
};
let pow = n => n * n;
console.log(pow(8));

注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适
箭头函数适合与 this 无关的回调: 定时器, 数组的方法回调
箭头函数不适合与 this 有关的回调: 事件回调, 对象的方法

const obj = {
name: 'lili',
getName: function(){
// this指向obj对象
this.name;
},
getName1: () => {
// this指向obj外层作用域,window
this.name;
}
}

箭头函数实践

  • 需求-1 点击 div 2s 后颜色变成『粉色』
//获取元素
let ad = document.getElementById('ad');
//绑定事件
ad.addEventListener("click", function () {
//保存 this 的值
let _this = this;
//定时器
setTimeout(function () {
//修改背景颜色 this
console.log(this); // window
_this.style.background = 'pink'; // 生效
// this.style.background = 'pink'; // 报错
}, 2000);
});

/********************************************/

ad.addEventListener("click", function () {
//保存 this 的值
// let _this = this;
//定时器
setTimeout(() => {
//修改背景颜色 this
// console.log(this);
// _this.style.background = 'pink';
this.style.background = 'pink'; // 生效
}, 2000);
});

  • 需求-2 从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25];
const result = arr.filter(function (item) {
if (item % 2 === 0) {
return true;
} else {
return false;
}
});

/********************************************/

const result = arr.filter(item => item % 2 === 0);
console.log(result);

函数参数的默认值设置

  • 形参初始值 具有默认值的参数, 一般位置要靠后(潜规则)
function add(a,b,c=10) {
return a + b + c;
}
let result = add(1,2);
console.log(result) // 13
  • 与解构赋值结合
function connect({host="127.0.0.1", username, password, port}){
console.log(host) // 127.0.0.1
console.log(username)
console.log(password)
console.log(port)
}
connect({
username: 'root',
password: 'root',
port: 3306
})

rest 参数

形式为"…变量名"

位置:函数声明的形参的位置

ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments

// ES5 获取实参的方式
function date(){
console.log(arguments); // typeof arguments-->Object
}
date('白芷','阿娇','思慧');

// rest 参数
function add(...args){
console.log(args); // [1,2,3,4,5]
}
add(1,2,3,4,5);

// rest 参数必须是最后一个形参
function minus(a,b,...args){
console.log(a,b,args); // args: [2,3,4,5,19]
}
minus(100,1,2,3,4,5,19);

rest参数只包括那些没有给出名称的参数arguments包含所有参数
arguments 对象不是真正的数组,而rest 参数是数组实例,可以直接应用sort, map, forEach, pop等方法
arguments 对象拥有一些自己额外的功能


spread 扩展运算符

扩展运算符

『…』 扩展运算符能将『数组』转换为逗号分隔的『参数序列』,对数组进行解包,它好比 rest 参数的逆运算

//声明一个数组 ...
const tfboys = ['易烊千玺','王源','王俊凯']; // 转=> '易烊千玺','王源','王俊凯'
// 声明一个函数
function chunwan(){
console.log(arguments);
}
// 位置:函数调用的实参
chunwan(...tfboys);// chunwan('易烊千玺','王源','王俊凯')

扩展运算符应用

  • 数组的合并
const kuaizi = ['王太利','肖央'];
const fenghuang = ['曾毅','玲花'];
const zuixuanxiaopingguo = kuaizi.concat(fenghuang); // es5
const zuixuanxiaopingguo = [...kuaizi, ...fenghuang];
console.log(zuixuanxiaopingguo); // ['王太利','肖央', '曾毅','玲花']
  • 数组的克隆
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua]; // ['E','G','M'] 浅拷贝
  • 将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
console.log(divs); // typeof --> object
const divArr = [...divs];
console.log(divArr); // [div, div, div]

Symbol

Symbol 基本使用

ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第种数据类型,是一种类似于字符串的数据类型。

Symbol 特点

    1. Symbol 的值是唯一的,用来解决命名冲突的问题
    1. Symbol 值不能与其他数据进行运算
    1. Symbol 定义的对象属性不能使用for…in循环遍历 ,但是可以使用Reflect.ownKeys来获取对象的所有键名
//创建 Symbol
let s1 = Symbol();
console.log(s1, typeof s1); // Symbol(), 'symbol' 不可见的唯一性
let s2 = Symbol('尚硅谷'); // Symbol('尚硅谷')
let s3 = Symbol('尚硅谷');
console.log(s2 === s3); // false

//Symbol.for 创建 Symbol.for中Symbol是对象,Symbol()中是函数,因此Symbol是函数对象
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5); // true

//不能与其他数据进行运算
let result = s1 + 100; // 报错

// USONB
// u undefined
// s string symbol
// o object
// n null number
// b boolean

Symbol 创建对象属性

使用场景:给对象添加属性和方法

//向对象中添加方法 up down
let game = {
name:'俄罗斯方块',
up: function(){},
down: function(){}
};

// 声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
};

game[methods.up] = function(){
console.log("我可以改变形状");
}
game[methods.down] = function(){
console.log("我可以快速下降!!");
}
console.log(game);

// 1.obj[Reflect.ownKeys(obj)[index]]()
//调用Symbol创建的函数
console.log(Reflect.ownKeys(game)) // ['name', 'up', 'down', Symbol(), Symbol()]
game[Reflect.ownKeys(game)[3]]() //up
game[Reflect.ownKeys(game)[4]]() //down


/*******************************************/

/*
不可以,Symbol()是个动态值,可以使用[Symbol()]
Symbol(): function(){
console.log("我可以发言")
},
*/
let youxi = {
name:"狼人杀",
[Symbol('say')]: function(){
console.log("我可以发言")
},
[Symbol('zibao')]: function(){
console.log('我可以自爆');
}
}

//2.Object.getOwnPropertySymbols(obj)[index](); 只获取Symbol类型
//调用Symbol创建的函数
//调用Symbol创建的函数
const a = Object.getOwnPropertySymbols(youxi); // [Symbol(say), Symbol(zibao)]
youxi[a[0]](); // 发言
youxi[a[1]](); // 自爆

/*******************************************/
let up1 = Symbol();
let down1 = Symbol();
let game1 = {
name: 'YY',
up1() {
console.log(1);
},
down1() {
console.log(2);
},
[up1]() {
console.log('向上');
},
[down1]() {
console.log('向下');
}
}

// 3.obj[方法名]()
//调用Symbol创建的函数
game1[up1](); // 向上
game1[down1](); // 向下

迭代器

Iterator

迭代器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作

  • ES6 创造了一种新的遍历命令 for…of 循环,Iterator 接口主要供 for…of 消费
  • 原生具备 iterator 接口的数据(可用 for of 遍历)
    • a) Array
    • b) Arguments
    • c) Set
    • d) Map
    • e) String
    • f) TypedArray
    • g) NodeList
  • 工作原理
    • a) 创建一个指针对象,指向当前数据结构的起始位置
    • b) 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
    • c) 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
    • d) 每调用 next 方法返回一个包含 value 和 done 属性的对象
//声明一个数组
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];

//使用 for...of 遍历数组
for(let v of xiyou){
console.log(v); // '唐僧','孙悟空','猪八戒','沙僧'
}

//使用 for...in 遍历数组
for(let v in xiyou){
console.log(v); // 0, 1, 2, 3
}

// Symbol.iterator对应一个函数
let iterator = xiyou[Symbol.iterator]();
//调用对象的next方法
console.log(iterator.next()); // {done: false, value: "唐僧"}
console.log(iterator.next()); // {done: false, value: "孙悟空"}
console.log(iterator.next()); // {done: false, value: "猪八戒"}
console.log(iterator.next()); // {done: false, value: "沙僧"}
console.log(iterator.next()); // {done: true, value: undefined}
console.log(iterator.next()); // {done: true, value: undefined}

iterator 接口:就是对象里面的一个属性,属性名字叫Symbol.iterator
注: 需要自定义遍历数据的时候,要想到迭代器。

Iterator自定义遍历数据

  • for…of遍历,每次返回结果是对象里属性的数组中的成员
    //声明一个对象
    const banji = {
    name: "终极一班",
    stus: [
    'xiaoming',
    'xiaoning',
    'xiaotian',
    'knight'
    ],
    // 方法
    [Symbol.iterator]() {
    //索引变量
    let index = 0;
    //
    let _this = this;
    // 返回一个对象
    return {
    // next方法
    next: function () {
    if (index < _this.stus.length) {
    const result = { value: _this.stus[index], done: false };
    //下标自增
    index++;
    //返回结果
    return result;
    }else{
    return {value: undefined, done: true};
    }
    }
    }
    }
    }

    //遍历这个对象
    for (let v of banji) {
    console.log(v); // 'xiaoming','xiaoning','xiaotian','knight'
    }

生成器

生成器函数声明和调用

生成器函数:是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同

  • 代码说明:
      1. * 的位置没有限制
      1. 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值
      1. yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次next方法,执行一段代码
      1. next方法可以传递实参,作为 上一个yield语句的返回值
//生成器其实就是一个特殊的函数
//异步编程
//yield:函数代码的分隔符
function * gen(){
console.log(111);
yield '一只没有耳朵';
console.log(222);
yield '一只没有尾部';
console.log(333);
yield '真奇怪';
console.log(444);
}
let iterator = gen();
// iterator.next() // 111
// iterator.next() // 222
//console.log(iterator.next()); // 111 {value: '一只没有耳朵', done: false}
//console.log(iterator.next()); // 222 {value: '一只没有尾部', done: false}
//console.log(iterator.next());
//console.log(iterator.next()); // 444 {value: undefined, done: true}

for(let v of gen()){
console.log(v);
}

生成器函数参数

function * gen(arg){
console.log(arg);
let one = yield 111;
console.log(one);
let two = yield 222;
console.log(two);
let three = yield 333;
console.log(three);
}

//执行获取迭代器对象
let iterator = gen('AAA');
// iterator.next() // AAA
// iterator.next('BBB') // BBB
console.log(iterator.next()); // AAA {value: 111, done: false}
//next方法可以传入实参
console.log(iterator.next('BBB')); // BBB {value: 222, done: false}
console.log(iterator.next('CCC'));
console.log(iterator.next('DDD')); // DDD {value: undefined, done: true}

生成器函数实例

  • 1s 后控制台输出 111 2s后输出 222 3s后输出 333
// 下面现象:回调地狱
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);

/*******************************/

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();
  • 模拟获取–>用户数据–>订单数据–>商品数据
function getUsers(){
setTimeout(()=>{
let data = '用户数据';
//调用 next 方法, 并且将数据传入
iterator.next(data);
}, 1000);
}

function getOrders(){
setTimeout(()=>{
console.log(users)
let data = '订单数据';
iterator.next(data);
}, 1000)
}

function getGoods(){
setTimeout(()=>{
let data = '商品数据';
iterator.next(data);
}, 1000)
}

function * gen(){
let users = yield getUsers();
let orders = yield getOrders(users);
console.log(orders)
let goods = yield getGoods();
}

//调用生成器函数
let iterator = gen();
iterator.next();

/** 执行结果:
* 用户数据
* 订单数据
* */


Promise

Promise基本语法

Promise 是 ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,用来封装异步操作并可以获取其成功失败的结果

// https://blog.csdn.net/weixin_45406850/article/details/122041770
//实例化 Promise 对象,接收一个参数(参数是一个函数,函数有两个形参,resolve, reject)
//函数里面封装一个异步操作
const p = new Promise(function(resolve, reject){
// 异步任务
setTimeout(function(){
let data = '数据库中的用户数据';
resolve(data); // 调用resolve函数表示成功,Promise状态成功

//let err = '数据读取失败';
//reject(err); // // 调用reject函数表示失败,Promise状态失败
}, 1000);
});

//resolve函数/reject函数改变Promise状态,改变状态后调用then方法里面的回调
//调用 promise 对象的 then 方法,接收两个参数,两个参数都是函数,每个函数都有一个形参
p.then(function(value){
// 成功
console.log(value); // 数据库中的用户数据
}, function(reason){
// 失败
console.error(reason);
})

Promise封装读取文件

  • 读取resources文件夹下的为学.md文件,在控制台输出
//1. 引入 fs 模块
const fs = require('fs');

//2. 调用方法读取文件
fs.readFile('./resources/为学.md', (err, data)=>{
//如果失败, 则抛出错误
if(err) throw err;
//如果没有出错, 则输出内容
console.log(data.toString());
});
// 执行上面代码:node 当前代码文件名

//3. 使用 Promise 封装
const p = new Promise(function(resolve, reject){
fs.readFile("./resources/为学.mda", (err, data)=>{
//判断如果失败
if(err) reject(err);
//如果成功
resolve(data);
});
});

p.then(function(value){
console.log(value.toString());
}, function(reason){
console.log("读取失败!!");
});

Promise封装AJAX

  • 原生的AJAX向url发送请求
// 接口地址: https://api.apiopen.top/getJoke
const p = new Promise((resolve, reject) => {
//1. 创建对象
const xhr = new XMLHttpRequest();

//2. 初始化,get请求
xhr.open("GET", "https://api.apiopen.top/getJ");

//3. 发送
xhr.send();

//4. 绑定事件, 处理响应结果
xhr.onreadystatechange = function () {
//判断
if (xhr.readyState === 4) {
//判断响应状态码 200-299
if (xhr.status >= 200 && xhr.status < 300) {
//表示成功
resolve(xhr.response);
} else {
//如果失败
reject(xhr.status);
}
}
}
})

//指定回调
p.then(function(value){
console.log(value);
}, function(reason){
console.error(reason);
});

Promise.prototype.then

then方法的返回结果是 Promise 对象, 对象状态回调函数的执行结果决定

//创建 promise 对象
const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('用户数据');
// reject('出错啦');
}, 1000)
});

//调用 then 方法 then方法的返回结果是 Promise 对象, 对象状态由回调函数的执行结果决定
//1. 如果回调函数中返回的结果是 非 promise 类型的属性, 状态为成功, 返回值为对象的成功的值
//2. 返回的结果是promise对象,内部promise返回的状态就决定then方法返回的promise状态
//3. 抛出错误,then返回值就是抛出的错误
const result = p.then(value => {
console.log(value);
//1. 非 promise 类型的属性
// return 'iloveyou';
//2. 是 promise 对象
// return new Promise((resolve, reject)=>{
// // resolve('ok');
// reject('error');
// });
//3. 抛出错误
// throw new Error('出错啦!');
throw '出错啦!';
}, reason=>{
console.warn(reason);
});

console.log(result); // Promise 对象

//链式调用,杜绝回调地狱
p.then(value=>{
// 里面放异步任务
}).then(value=>{
// 里面放异步任务
});

实践-读取多个文件

  • 依次读取三个文件内容,将三个内容合在一起输出
//引入 fs 模块
const fs = require("fs");

// 回调地狱,代码往前缩紧
// fs.readFile('./resources/为学.md', (err, data1)=>{
// fs.readFile('./resources/插秧诗.md', (err, data2)=>{
// fs.readFile('./resources/观书有感.md', (err, data3)=>{
// let result = data1 + '\r\n' +data2 +'\r\n'+ data3;
// console.log(result);
// });
// });
// });

//使用 promise 实现,异步任务串联
const p = new Promise((resolve, reject) => {
fs.readFile("./resources/为学.md", (err, data) => {
resolve(data);
});
});

p.then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/插秧诗.md", (err, data) => {
resolve([value, data]);
});
});
}).then(value => {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
//压入
value.push(data);
resolve(value);
});
})
}).then(value => {
console.log(value.join('\r\n'));
});

Promise.prototype.catch

const p = new Promise((resolve, reject)=>{
setTimeout(()=>{
//设置 p 对象的状态为失败, 并设置失败的值
reject("出错啦!");
}, 1000)
});

// p.then(function(value){
//
// }, function(reason){
// console.error(reason);
// });

p.catch(function(reason){
console.warn(reason);
});

集合Set

集合介绍

ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯 一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』『for…of…』进 行遍历。
集合的属性和方法:

    1. size 返回集合的元素个数
    1. add 增加一个新元素,返回当前集合
    1. delete 删除元素,返回 boolean 值
    1. has 检测集合中是否包含某个元素,返回 boolean 值
    1. clear 清空集合,返回
//声明一个空set
let s = new Set();
//console.log(s, typeof s) // Set(0){size: 0} , object
//创建一个非空集合,会去重
let s2 = new Set(['大事儿','小事儿','好事儿','坏事儿','小事儿']);
//console.log(s2) // Set(4){'大事儿', '小事儿', '好事儿', '坏事儿'}
console.log(...s2) // 大事儿 小事儿 好事儿 坏事儿

//元素个数
// console.log(s2.size); // 4
//添加新的元素
// s2.add('喜事儿');
//删除元素
// s2.delete('坏事儿');
//检测
// console.log(s2.has('糟心事')); // false
//清空
// s2.clear();
// console.log(s2);

for(let v of s2){
console.log(v);
}

Set实践

let arr = [1,2,3,4,5,4,3,2,1];
//1. 数组去重
// let result = [...new Set(arr)];
// console.log(result);

//2. 交集
let arr2 = [4,5,6,5,6];
// let result = [...new Set(arr)].filter(item => {
// let s2 = new Set(arr2);// 4 5 6
// if(s2.has(item)){
// return true;
// }else{
// return false;
// }
// });
// let result = [...new Set(arr)].filter(item => new Set(arr2).has(item));
// console.log(result);

//3. 并集
// let union = [...new Set([...arr, ...arr2])];
// console.log(union);

//4. 差集 在arr中有,在arr2中没有
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(diff);

Map

ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』『for…of…』进行遍历。
Map 的属性和方法:

    1. size 返回 Map 的元素个数
    1. set 增加一个新元素,返回当前 Map
    1. get 返回键名对象的键值
    1. has 检测 Map 中是否包含某个元素,返回 boolean 值
    1. clear 清空集合,返回 undefined
    1. delete 根据键名删除
//声明 Map
let m = new Map();

//添加元素
m.set('name','尚硅谷'); // { key: name,value: 尚硅谷 }
m.set('change', function(){
console.log("我们可以改变你!!");
});
let key = {
school : 'ATGUIGU'
};
m.set(key, ['北京','上海','深圳']); {key: {school : 'ATGUIGU'}, value: ['北京','上海','深圳']}

//size
// console.log(m.size); // 3

//删除
// m.delete('name');

//获取
// console.log(m.get('change'));
// console.log(m.get(key));

//清空
// m.clear();

//遍历
for(let v of m){
console.log(v); // ['name', 尚硅谷] ...
}

// console.log(m);

class 类

ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:

    1. class 声明类
    1. constructor 定义构造函数初始化
    1. extends 继承父类
    1. super 调用父级构造方法
    1. static 定义静态方法和属性
    1. 父类方法可以重写

class介绍

/*********** ES5 **********/
//手机
function Phone(brand, price){
this.brand = brand;
this.price = price;
}

//添加方法
Phone.prototype.call = function(){
console.log("我可以打电话!!");
}

//实例化对象
let Huawei = new Phone('华为', 5999);
Huawei.call();
console.log(Huawei);

/*********** ES6 **********/
//class
class Shouji{
//构造方法 名字不能修改
constructor(brand, price){
this.brand = brand;
this.price = price;
}

//对象方法必须使用该语法, 不能使用 ES5 的对象完整形式
call(){
console.log("我可以打电话!!");
}
}

let onePlus = new Shouji("1+", 1999);

console.log(onePlus)

class静态成员

/*********** ES5 **********/
function Phone(){}
// name是函数对象上属性
Phone.name = '手机';
Phone.change = function(){
console.log("我可以改变世界");
}
Phone.prototype.size = '5.5inch';

// nokia实例对象没有构造函数Phone对象上的属性
let nokia = new Phone();

console.log(nokia.name); // undefined
console.log(nokia.size); // 5.5inch

/*********** ES6 **********/
class Phone{
//静态属性
static name = '手机';
static change(){
console.log("我可以改变世界");
}
}

let nokia = new Phone();
console.log(nokia.name); // undefined
console.log(Phone.name); // 手机

ES5构造函数继承

//手机  //父类
function Phone(brand, price){
this.brand = brand;
this.price = price;
}

Phone.prototype.call = function(){
console.log("我可以打电话");
}

//智能手机 //子类
function SmartPhone(brand, price, color, size){
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}

//设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = SmartPhone; // 矫正

//声明子类的方法
SmartPhone.prototype.photo = function(){
console.log("我可以拍照")
}

SmartPhone.prototype.playGame = function(){
console.log("我可以玩游戏");
}

const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch');

console.log(chuizi);

class类的继承

class Phone{
//构造方法
constructor(brand, price){
this.brand = brand;
this.price = price;
}
//父类的成员属性
call(){
console.log("我可以打电话!!");
}
}
// 子类SmartPhone继承父类Phone中的属性和方法
class SmartPhone extends Phone {
//构造方法
constructor(brand, price, color, size){
super(brand, price);// Phone.call(this, brand, price)
this.color = color;
this.size = size;
}

photo(){
console.log("拍照");
}

playGame(){
console.log("玩游戏");
}

//方法重写,子类不可以直接调用父类的重名方法
call(){
console.log('我可以进行视频通话');
}

static connect(){
console.log('我可以建立连接')
}
}
//实例化对象
const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch');
//调用子类方法
xiaomi.playGame();
//调用重写方法
xiaomi.call();
//调用静态方法
SmartPhone.connect();

class的get和set

// get 和 set  
class Phone{
get price(){
console.log("价格属性被读取了");
return 'iloveyou';
}

// 函数必须有参数
set price(newVal){
console.log(newVal);
console.log('价格属性被修改了');
}
}

//实例化对象
let s = new Phone();

console.log(s.price); // 价格属性被读取了 iloveyou
s.price = 'free'; // free 价格属性被修改了

ES6的数值扩展

// 1. Number.EPSILON 是 JavaScript 表示的最小精度
// EPSILON 属性的值接近于 2.2204460492503130808472633361816E-16
function equal(a, b){
if(Math.abs(a-b) < Number.EPSILON){
return true;
}else{
return false;
}
}
console.log(0.1 + 0.2 === 0.3); // false
console.log(equal(0.1 + 0.2, 0.3)) // true

// 2.二进制和八进制
let b = 0b1010; // 二进制 10
let o = 0o777; // 八进制 511
let d = 100; // 十进制 100
let x = 0xff; // 十六进制 255

// 3.Number.isFinite 检测一个数值是否为有限数
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(100/0)); // false
console.log(Number.isFinite(Infinity)); // false

// 4.Number.isNaN 检测一个数值是否为 NaN
console.log(Number.isNaN(123)); // false

// 5.Number.parseInt Number.parseFloat字符串转整数
console.log(Number.parseInt('5211314love')); // 5211314
console.log(Number.parseFloat('3.1415926神奇')); // 3.1415926

// 6.Number.isInteger 判断一个数是否为整数
console.log(Number.isInteger(5)); // true

// 7.Math.trunc 将数字的小数部分抹掉
console.log(Math.trunc(3.5)); // 3

// 8. Math.sign 判断一个数到底为正数 负数 还是零
console.log(Math.sign(100)); // 1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-20000)); // -1

ES6的对象方法扩展

// 1. Object.is 比较两个值是否严格相等,与『===』行为基本一致
console.log(Object.is(120, 120)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(Object.is(+0, -0)); // false
console.log(Object.is(+0, 0)); // true
console.log(NaN === NaN); // false
console.log(+0 === -0); true

// 2.Object.assign 对象的合并
const config1 = {
host: 'localhost',
port: 3306,
name: 'root',
pass: 'root',
test: 'test'
};
const config2 = {
host: 'http://atguigu.com',
port: 33060,
name: 'atguigu.com',
pass: 'iloveyou',
test2: 'test2'
}
// 后面对象会覆盖与前面对象属性名形同的属性值,前后对象不同的将会保留
console.log(Object.assign(config1, config2));

// 3.Object.setPrototypeOf 设置原型对象 Object.getPrototypeof
// __proto__、setPrototypeOf、 getPrototypeOf 可以直接设置对象的原型
const school = {
name: '尚硅谷'
}
const cities = {
xiaoqu: ['北京','上海','深圳']
}
Object.setPrototypeOf(school, cities); // 设置原型对象
console.log(Object.getPrototypeOf(school)); // 获取原型对象
console.log(school);

模块化

模块化介绍

模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来
模块化的优势有以下几点:

    1. 防止命名冲突
    1. 代码复用
    1. 高维护性

ES6 之前的模块化规范有:

    1. CommonJS => NodeJS、Browserify
    1. AMD => requireJS
    1. CMD => seaJS

模块功能主要由两个命令构成:export 和 import

  • export 命令用于规定模块的对外接口
    • 分别暴露
    • 统一暴露
    • 默认暴露
// m1.js文件
// 分别暴露
export let school = '尚硅谷';

export function teach() {
console.log("我们可以教给你开发技能");
}
// m2.js文件
// 统一暴露
let school = '尚硅谷';

function findJob(){
console.log("我们可以帮助你找工作!!");
}

export {school, findJob};
// m3.js文件
// 默认暴露
export default {
school: 'ATGUIGU',
change: function(){
console.log("我们可以改变你!!");
}
}
// 引入 m1.js 模块内容,* --> mi文件中的所有内容存在对象中
import * as m1 from "./src/js/m1.js";
// {school: '尚硅谷', teach: function(...)}
m1.teach()

// 引入 m2.js 模块内容
import * as m2 from "./src/js/m2.js";
m2.findJob()

// 引入 m3.js
import * as m3 from "./src/js/m3.js";
m3.default.change()
  • import 命令用于输入其他模块提供的功能
    • 通用的导入方式,* 引入文件中所有内容,放到对象中
    • 解构赋值形式,as + 别名:给引入文件中的属性起别名,解决属性名重复问题
    • 简便形式,针对默认暴露
// 通用的导入方式, * 引入文件中所有内容,放到对象中
import * as m1 from "./src/js/m1.js";

// 解构赋值形式,
import {school, teach} from "./src/js/m1.js";
import {school as guigu, findJob} from "./src/js/m2.js";
import {default as m3} from "./src/js/m3.js"; // 固定写法,default是一个对象,需要起别名
teach()
m3.change()

//3. 简便形式 针对默认暴露
import m3 from "./src/js/m3.js";
m3.change()

babel对ES6模块化代码的转换

    1. 安装工具 npm i babel-cli(babel命令行工具) babel-preset-env(预设包) browserify(打包工具)(webpack) -D
    1. 编译 npx babel src/js -d dist/js –presets=babel-preset-env
    1. 打包 npx browserify dist/js/app.js -o dist/bundle.js
    1. 引入dist/bundle.js文件就可以src/js中文件的内容

ES7

Array.prototype.includes

  • includes 方法用来检测数组中是否包含某个元素,返回布尔类型值
// includes
const mingzhu = ['西游记','红楼梦','三国演义','水浒传'];
//判断
console.log(mingzhu.includes('西游记')); // true
console.log(mingzhu.includes('金瓶梅')); // false
// indexOf判断如果有返回结果是下标,反之返回-1
console.log(mingzhu.indexOf('红楼梦')) // 1

指数操作符

  • 指数运算符「**」,用来实现幂运算,功能与 Math.pow 结果相同
console.log(2 ** 3);// 8
console.log(Math.pow(2, 3)); // 8

ES8

async 和 await

async 和 await 两种语法结合可以让异步代码同步代码一样

async 函数

  • async 函数的返回值为 promise 对象,
    • 如果return直接返回字符串、数组等【不是Promise 类型的对象】, 那么返回的结果就是成功 Promise 对象
    • 如果抛出错误, 返回的结果是一个失败的 Promise
    • 返回的结果如果是一个 Promise 对象,promise 对象的结果由 async 函数执行的返回值决定
 //async 函数
async function fn(){
// 返回一个字符串
// return '尚硅谷';
// 返回的结果不是一个 Promise 类型的对象, 返回的结果就是成功 Promise 对象
// return;
//抛出错误, 返回的结果是一个失败的 Promise
// throw new Error('出错啦!');
//返回的结果如果是一个 Promise 对象
return new Promise((resolve, reject)=>{
resolve('成功的数据');
// reject("失败的错误");
});
}

const result = fn();

//调用 then 方法
result.then(value => {
console.log(value);
}, reason => {
console.warn(reason);
})

await 表达式

  • await 必须写在 async 函数中
  • await 右侧的表达式一般为 promise 对象
  • await 返回的是 promise 成功的值
  • await 的 promise 失败了, 就会抛出异常, 需要通过 try…catch 捕获处理
//创建 promise 对象
const p = new Promise((resolve, reject) => {
// resolve("用户数据");
reject("失败啦!");
})

// await 要放在 async 函数中.
async function main() {
try {
// 成功
let result = await p;
console.log(result);
} catch (e) {
// 失败
console.log(e);
}
}
//调用函数
main();

async和await结合实例

  • 读取文件中的内容
//1. 引入 fs 模块
const fs = require("fs");

//读取『为学』
function readWeiXue() {
return new Promise((resolve, reject) => {
fs.readFile("./resources/为学.md", (err, data) => {
//如果失败
if (err) reject(err);
//如果成功
resolve(data);
})
})
}

function readChaYangShi() {
return new Promise((resolve, reject) => {
fs.readFile("./resources/插秧诗.md", (err, data) => {
//如果失败
if (err) reject(err);
//如果成功
resolve(data);
})
})
}

function readGuanShu() {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
//如果失败
if (err) reject(err);
//如果成功
resolve(data);
})
})
}

//声明一个 async 函数
async function main(){
//获取为学内容
let weixue = await readWeiXue();
//获取插秧诗内容
let chayang = await readChaYangShi();
// 获取观书有感
let guanshu = await readGuanShu();

console.log(weixue.toString());
console.log(chayang.toString());
console.log(guanshu.toString());
}

main();
  • 发送 AJAX 请求
// 发送 AJAX 请求, 返回的结果是 Promise 对象
function sendAJAX(url) {
return new Promise((resolve, reject) => {
//1. 创建对象
const x = new XMLHttpRequest();

//2. 初始化
x.open('GET', url);

//3. 发送
x.send();

//4. 事件绑定
x.onreadystatechange = function () {
if (x.readyState === 4) {
if (x.status >= 200 && x.status < 300) {
//成功啦
resolve(x.response);
}else{
//如果失败
reject(x.status);
}
}
}
})
}

//promise then 方法测试
// sendAJAX("https://api.apiopen.top/getJoke").then(value=>{
// console.log(value);
// }, reason=>{})

// async 与 await 测试 axios
async function main(){
//发送 AJAX 请求
let result = await sendAJAX("https://api.apiopen.top/getJoke");
//再次测试
let tianqi = await sendAJAX('https://www.tianqiapi.com/api/?version=v1&city=%E5%8C%97%E4%BA%AC&appid=23941491&appsecret=TXoD5e8P')

console.log(tianqi);
}

main();

对象方法扩展

Object.values 和 Object.entries

  • Object.values()方法返回一个给定对象的所有可枚举属性值的数组
  • Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组
//声明对象
const school = {
name:"尚硅谷",
cities:['北京','上海','深圳'],
xueke: ['前端','Java','大数据','运维']
};
//获取对象所有的键
console.log(Object.keys(school)); // ['name', 'cities', 'xueke']
//获取对象所有的值
console.log(Object.values(school)); ['尚硅谷', ['北京', '上海', '深圳'], ['前端','Java','大数据','运维']]
//entries
console.log(Object.entries(school)); [['name', '尚硅谷'], ['cities', ['北京', '上海', '深圳']], ['xueke', ['前端','Java','大数据','运维']]]
//创建 Map
const m = new Map(Object.entries(school));
console.log(m.get('cities')); // ['北京', '上海', '深圳']

Object.getOwnPropertyDescriptors

  • 该方法返回指定对象所有自身属性的描述对象
//声明对象
const school = {
name:"尚硅谷",
cities:['北京','上海','深圳'],
xueke: ['前端','Java','大数据','运维']
};
console.log(Object.getOwnPropertyDescriptors(school));

const obj = Object.create(null, {
name: {
//设置值
value: '尚硅谷',
//属性特性
writable: true, // 是否可写
configurable: true, // 是否可删
enumerable: true // 是否可枚举
}
});

ES9

Rest/Spread 属性

Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,在 ES9 中为对象提供了像数组一样的 rest 参数扩展运算符

//rest 参数
function connect({host, port, ...user}){
console.log(host);
console.log(port);
console.log(user);
}

connect({
host: '127.0.0.1',
port: 3306,
username: 'root',
password: 'root',
type: 'master'
});


//对象合并
const skillOne = {
q: '天音波'
}

const skillTwo = {
w: '金钟罩'
}

const skillThree = {
e: '天雷破'
}
const skillFour = {
r: '猛龙摆尾'
}

// ...skillOne => q: '天音波', w: '金钟罩'
const mangseng = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};

console.log(mangseng)

正则表达式

式命名捕获组

ES9 允许命名捕获组使用符号『?<name>』,这样获取捕获结果可读性更强

//声明一个字符串
let str = '<a href="http://www.atguigu.com">尚硅谷</a>';

// 提取 url 与 『标签文本』
const reg = /<a href="(.*)">(.*)<\/a>/;

// 执行
const result = reg.exec(str);

console.log(result[0]); // <a href="http://www.atguigu.com">尚硅谷</a>
console.log(result[1]); // http://www.atguigu.com
console.log(result[2]); // 尚硅谷


let str1 = '<a href="http://www.atguigu.com">尚硅谷</a>';
//分组命名,返回的数据会多一个groups
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;

const result = reg.exec(str1);

console.log(result.groups.url); // http://www.atguigu.com

console.log(result.groups.text); // 尚硅谷

反向断言

ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。

// 提取字符串的555
//声明字符串
let str = 'JS5211314你知道么555啦啦啦';
//正向断言
const reg = /\d+(?=啦)/;
const result = reg.exec(str);
console.log(result[0]); // 555

//反向断言
const reg1 = /(?<=么)\d+/;
const result1 = reg.exec(str);
console.log(result1[0]); // 555

dotAll 模式

正则表达式中点.匹配除回车外的任何单字符,标记『s』改变这种行为,允许行终止符出现

// ul标签中的电影名称和上映日期提取出来存到一个对象中
//dot . 元字符 除换行符以外的任意单个字符
let str = `
<ul>
<li>
<a>肖生克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>`;
//声明正则
// const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/;
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
//执行匹配
// const result = reg.exec(str);
let result;
let data = [];
while(result = reg.exec(str)){
data.push({title: result[1], time: result[2]});
}
//输出结果
console.log(data);

ES10

Object.fromEntries

将二维数组转换为对象,与Object.entries互为逆运算

//二维数组
const result = Object.fromEntries([
['name','尚硅谷'],
['xueke', 'Java,大数据,前端,云计算']
]);
console.log(result) // { name: '尚硅谷', xueke: 'Java,大数据,前端,云计算' }

//Map
const m = new Map();
m.set('name','ATGUIGU');
const result1 = Object.fromEntries(m);
console.log(result1) // { name: "ATGUIGU" }

//Object.entries ES8 将对象转为二维数组
const arr = Object.entries({
name: "尚硅谷"
})
console.log(arr); [['name', '尚硅谷']]

trimStart 和 trimEnd

  • trimStart 清除字符串左侧空白的字符
  • trimEnd 清除字符串右侧空白的字符
// trim:清除字符串两侧空白的字符
let str = ' iloveyou ';

console.log(str);
console.log(str.trimStart());
console.log(str.trimEnd());

Array.prototype.flat 与 flatMap

将多维数组转化为低位数组

//将多维数组转化为低位数组
const arr = [1,2,3,4,[5,6]];
console.log(arr.flat()); // [1,2,3,4,5,6]
const arr1 = [1,2,3,4,[5,6,[7,8,9]]];
//参数为深度 是一个数字,2表示两层数组,默认值是1
console.log(arr1.flat(2)); // [1,2,3,4,5,6,[7,8,9]]

//flatMap
const arr2 = [1,2,3,4];
const result1 = arr2.map(item => [item * 10]);
console.log(result1); // [[10], [20], [30], [40]]
const result = arr2.flatMap(item => [item * 10]);
console.log(result); [10,20,30,40];

Symbol.prototype.description

获取Symbol的字符串描述

//创建 Symbol
let s = Symbol('尚硅谷');

console.log(s.description); // 尚硅谷

ES11

私有属性

class Person{
//公有属性
name;
//私有属性
#age;
#weight;
//构造方法
constructor(name, age, weight){
this.name = name;
this.#age = age;
this.#weight = weight;
}

intro(){
console.log(this.name);
console.log(this.#age);
console.log(this.#weight);
}
}

//实例化
const girl = new Person('晓红', 18, '45kg');

console.log(girl.name); // 晓红
console.log(girl.#age); // 报错
console.log(girl.#weight); // 报错

girl.intro();

Promise.allSettled

返回的结果始终是成功(resolve)的,成功的值是每一个promise对象执行的结果的值和状态

//声明两个promise对象
const p1 = new Promise((resolve, reject)=>{
setTimeout(()=>{
resolve('商品数据 - 1');
},1000)
});

const p2 = new Promise((resolve, reject)=>{
setTimeout(()=>{
reject('出错啦!');
},1000)
});

//调用 allsettled 方法
const result = Promise.allSettled([p1, p2]);
console.log(result); // p1:resolve,p2:reject
// Promise.all是根据promise对象的状态决定的,都成功Promise.all才成功,成功的值是数组promise成功的值组成的数组;否则失败,返回的状态是reject,失败的值是数组中出错的promise的失败的值
const res = Promise.all([p1, p2]);
console.log(res);

String.prototype.matchAll

页面中提取数据

let str = `<ul>
<li>
<a>肖生克的救赎</a>
<p>上映日期: 1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期: 1994-07-06</p>
</li>
</ul>`;

//声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg

//调用方法
const result = str.matchAll(reg);

for(let v of result){
console.log(v);
}

const arr = [...result];

console.log(arr);

可选链操作符

符号:?.,使用情况:获取对象层级较深的属性值,解决层层判断的烦恼

 // ?.
function main(config){
// const dbHost = config && config.db && config.db.host;
const dbHost = config?.db?.host;

console.log(dbHost);
}

main({
db: {
host:'192.168.1.100',
username: 'root'
},
cache: {
host: '192.168.1.200',
username:'admin'
}
})

动态import加载

按需加载

// hello.js文件
export function hello(){
alert('Hello');
}
// import * as m1 from "./hello.js"; // 静态
//获取元素
const btn = document.getElementById('btn');

btn.onclick = function(){
// 动态,import函数返回的结果是一个promise对象,promise对象成功的值就是引入的文件中暴露的对象
import('./hello.js').then(module => {
module.hello();
});
}

BigInt 大整形

更大的数值运算
声明方式:在普通数字后面加n

//大整形
let n = 521n;
console.log(n, typeof(n)); // 521n 'bigInt'

//BigInt()函数只能针对整数,将其转为bigInt类型
let n = 123;
console.log(BigInt(n)); // 123n
console.log(BigInt(1.2)); // 报错

//大数值运算
let max = Number.MAX_SAFE_INTEGER; // 最大安全整数
console.log(max);
console.log(max + 1);
console.log(max + 2);

console.log(BigInt(max))
console.log(BigInt(max) + BigInt(1))
console.log(BigInt(max) + BigInt(2))

globalThis

始终指向全局对象