【前端 JavaScript 高级】 05 - ES6 - ES
1. ECMASript 相关介绍
1.1 什么是 ECMA
什么是ECMA-
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会, 这个组织的目标是评估、开发和认可电信和计算机标准。 1994 年后该组织改名为
Ecma
国际。
1.2 什么是 ECMAScript
- ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。
1.3 什么是 ECMA-262
- Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个, 所有标准列表查看
1.4 ECMA-262 历史
版 本 | 时 间 | 事 项 |
---|---|---|
第 1 版 |
1997 年 | 制定了语言的基本语法 |
第 2 版 |
1998 年 | 较小改动 |
第 3 版 |
1999 年 | 引入正则、异常处理、格式化输出等。 IE 开始支持 |
第 4 版 |
2007 年 | 过于激进,未发布 |
第 5 版 |
2009 年 | 引入严格模式、 JSON,扩展对象、数组、原型、字符串、日期方法 |
第 6 版 |
2015 年 | 模块化、面向对象语法、Promise、箭头函数、 let、const、数组解构赋值等等 |
第 7 版 |
2016 年 | 幂运算符、数组扩展、Async/await 关键字 |
第 8 版 |
2017 年 | Async/await、字符串扩展 |
第 9 版 |
2018 年 | 对象解构赋值、正则扩展 |
第 10 版 |
2019 年 | 扩展对象、数组方法 |
ES.next |
动态指向下一个版本 |
注:从 ES6 开始,每年发布一个版本,版本号比年份最后一位大 1
1.5 谁在维护 ECMA-262
-
TC39(Technical Committee 39)
是推进ECMAScript
发展的委员会。其会员都是公司(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。 TC39 定期召开会议,会议由会员公司的代表与特邀专家出席。
1.6 为什么要学习 ES6
-
ES6
的版本变动内容最多,具有里程碑意义; -
ES6
加入许多新的语法特性,编程实现更简单、高效; -
ES6
是前端发展趋势,就业必备技能。
1.7 ES6 兼容性
2. ECMASript 6 新特性
2.1 let关键字
let
关键字用来声明变量,使用 let
声明的变量有几个特点:
-
不允许重复声明变量;
-
有块儿级作用域;
-
不存在变量提升(预解析),如之前使用
var
关键字声明变量时会将使用var
声明 提到最前面。var num = 10; 变量提升之后 是将 var num; 提到最前面之后在进行赋值操作
; -
不影响作用域链。
应用场景:以后声明变量使用 let 就对了
。
let a;
let b, c, d;
let e = 100;
let f = 222, g = 'love you', h = [];
// 1. 变量不能重复声明
// let star = '李连杰';
// let star = '吴京';
// 2. 使用var声明的变量是可以重复的
// var star = '李连杰';
// var star = '吴京';
// 2. 块级作用域全局 函数,eval
{
let boy = 'lyp';
}
console.log(boy); // 这里是访问不到的
// 3. 不存在变量提升
var song = '666';
// 4. 不影响作用域链
{
let school = '王者学院';
function fun() {
console.log(school);
}
}
2.2 const 关键字
const
关键字用来声明常量,const
声明有以下特点:
-
声明必须赋初始值;
-
标识符一般为大写;
-
不允许重复声明;
-
值不允许修改;
-
块儿级作用域。
注意: 对象属性修改和数组元素变化不会触发 const 错误。 应用场景:声明对象类型使用 const,非对象类型声明选择 let
。
// 声明常量
const SCHOOL = '王者学院';
// 1. 常量的声明一定要初始化
const A;
// 2. 常量的值不能修改
SCHOOL = '666';
// 3. const 有块级作用域
{
const UZI = '乌兹';
}
console.log(UZI);
// 4. 一般常量标识符需要大写
// 5. 对于数组和对象的元素修改,不算对常量的修改,不会报错
const TEAM = ['王大', '王二', '王三'];
TEAM.push('王四');
2.3 变量的解构赋值
-
ES6
允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
// ES6允许按照一定的模式从数组或者对象中进行提取值,对变量进行赋值
// 这就被称为解构赋值
// 1. 数组的解构
const F4 = ['宋小宝', '小沈阳', '刘能', '赵四'];
let [song, xiao, liu, zhao] = F4;
console.log(song);
console.log(xiao);
console.log(liu);
console.log(zhao);
// 2. 对象的解构
const obj = {
name: '赵本山',
age: '66',
xiaopin: function () {
console.log('我会演小品');
}
}
let {xiaopin} = obj;
// 解构之后直接调用
xiaopin();
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式。
2.4 模板字符串
模板字符串(template string)
是增强版的字符串, 用反引号(`)标识,特点:
-
字符串中可以出现换行符;
-
可以使用
${xxx}
形式输出变量。
// ES6中引入新的声明字符串的方式 `` '' ""
// 1. 声明
let str = `我是一个字符串`;
console.log(str, typeof str);
// 2. 使用模板字符串之后可以直接出现换行符
let star = `
<h1>张三</h1>
<h1>李四</h1>
<h1>王五</h1>
<h1>赵六</h1>
`;
console.log(star);
// 3. 模板字符串中的字符串拼接
let lovest = '欢欢'
let starest = `${lovest}是我最喜欢的人`;
console.log(starest);
注意:当遇到字符串与变量拼接的情况使用模板字符串。
2.5 简化对象写法
-
ES6
允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = '王者学院';
let slogon = '666';
let improve = function () {
console.log('可以提高你的技能');
}
//属性和方法简写
let atguigu = {
name,
slogon,
improve,
change() {
console.log('可以改变你')
}
};
注意:对象简写形式简化了代码,所以以后用简写就对了。
2.6 箭头函数
-
ES6
中的箭头函数()=>
定义函数。
/*******************************************************************************************/
// ES6中的箭头函数 ()=> 定义函数
/**
* 传统的声明函数的方式
*/
let fn = function (a, b) {
return a + b;
};
console.log(fn(12, 32));
/**
* 使用箭头函数的方式声明一个函数
*/
let fun = (a, b) => {
return a + b;
};
// 调用函数
console.log(fun(1, 3));
/*******************************************************************************************/
/**
* 箭头函数 和 传统函数声明的区别
*
* 1. this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的
*
*/
function getName() {
console.log(this.name); //
}
let getNameTo = () => {
console.log(this.name);//
};
// 设置 widow 对象的name属性
window.name = 'windowyo';
const SCHOOL = {
name: 'ATLYP'
};
// 使用call调用改变它的this指向
getName.call(SCHOOL); // 调用传统方式声明的函数并改变其this指向 ATLYP 指向改变了
getNameTo.call(SCHOOL); // 调用箭头函数方式声明的函数并改变其this指向 windowyo 指向未改变
/*******************************************************************************************/
/**
* 箭头函数不能作为构造函数创建对象的
* @param name
* @param age
* @constructor
*/
let Person = (name, age) => {
this.name = name;
this.age = age;
};
// let p = new Person('张三', 23); // Person is not a constructor 箭头函数声明的构造函数不能创建对象
/*******************************************************************************************/
/**
* 箭头函数中是不能使用arguments变量
*/
function useArguments(a, b) {
console.log(arguments);
}
useArguments(12, 34);
let notArguments = (a, b) => {
// console.log(arguments);
}
notArguments(23, 56); // arguments is not defined
/*******************************************************************************************/
/**
* 箭头函数的简写
*
* 1. 省略小括号,当形参有且只有一个的时候
*
* 2. 可以省略花括号 , 当代码体中只有一条语句的时候,此时return也必须省略,而且函数的执行结果就是函数的返回值
*/
// 1. 省略小括号
let add = n => {
return n + n;
};
console.log(add(20)); // 40
// 2. 省略花括号
let pow = n => n * n;
console.log(pow(20)); // 400
/*******************************************************************************************/
箭头函数的注意点:
-
如果形参只有一个,则小括号可以省略;
-
函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果;
-
箭头函数 this 指向声明时所在作用域下
this
的值;; -
箭头函数不能作为构造函数实例化;
-
不能使用
arguments
。 -
箭头函数中this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的
。
箭头函数和实际的应用场景
-
箭头函数中this是静态的。 this始终是指向函数 声明时 所在作用域 的 this 的值 。无论怎么改变其this都是不会改变的
。 -
箭头函数适合与this无关的回调、定时器、数组的方法回调。
-
箭头函数不适合与this有关的回调,如 事件回调、对象的方法。
// 获取dom元素
let ad = document.querySelector('.ad');
/**
* 箭头函数不适合作为事件回调 ,但是适合定时器,数组方法等
*/
ad.addEventListener('click', function () {
// 事先保存一个 this
let _this = this;
// 在点击盒子之后2秒改变盒子的颜色
setTimeout(() => {
// 注意这里的this 因为这里是一个箭头函数,箭头函数中的this是静态的,其是始终指向声明时所在作用域的this的值
// _this.style.backgroundColor = 'pink';
this.style.backgroundColor = 'pink';
}, 2000)
});
// 箭头函数适合与 this 无关的回调、定时器、数组的方法回调
// 过滤数组中的元素,找出其中的偶数
let arr = [1, 2, 3, 9, 90, 78];
/**
*
* 使用传统function声明函数的方式
* */
let newArrFunction = arr.filter(function (item) {
return item % 2 === 0;
});
console.log(newArrFunction);
/**
* 使用箭头函数的方式
* @type {number[]}
*/
let newArr = arr.filter(item => item % 2 === 0);
console.log(newArr);
// 箭头函数不适合与this有关的回调,如事件回调、对象的方法
注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合适。
2.7 rest 参数
-
ES6
引入rest
参数,用于获取函数的实参,用来代替arguments
。
// ES6引入rest参数用于获取函数的实参,用来代替arguments
// ES5中获取实参的方式
function es5Arguments(a, b, c) {
console.log(arguments); // 这是一个对象 {1, 2, 3}
}
es5Arguments(1, 2, 3);
// ES6 中的rest参数
/**
* rest参数必须放在参数的最后
*/
function es6Rest(a, b, ...args) {
console.log(a);
console.log(b);
console.log(args); // 这是一个数组 [56, 89, 784, 12, 456, 456]
}
es6Rest(12, 23, 56, 89, 784, 12, 456, 456);
注意: rest 参数非常适合不定个数参数函数的场景。
ES6中函数参数的默认值
-
ES6中为形参赋初始值 , 具有默认值的形参的位置一般靠后。
-
参数默认值可以与解构赋值结合使用。
/**
* ES6中为形参赋初始值 , 具有默认值的形参的位置一般靠后
* @param a
* @param b
* @param c
*/
function add(a = 10, b = 20, c = 30) {
return a + b + c;
}
// 2. 参数默认值可以与解构赋值结合使用
function connect({host = '45.56.47.48', username = 'root', password = '123456', port = '3306'}) {
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
connect({
host: '127.0.0.1',
username: 'root',
password: 'root',
port: '3306'
});
2.8 spread 扩展运算符
- 扩展运算符(
spread
)也是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
// 扩展运算符将数组转换为逗号分隔的参数序列
const tfBoy = ['易烊千玺', '王源', '张三'];
function shaBi() {
console.log(arguments);
}
shaBi(...tfBoy);
// 展开运算符 里面有引用数据类型的话也是一个浅拷贝
let oldArr = ['j', 'f', 'r', 'p'];
let newArr = ['j', 'a', 'v', 'a'];
newArr = oldArr.concat(newArr);
console.log(newArr);
let conCatArr = [...oldArr, ...newArr];
console.log(conCatArr);
// 将伪数组转换为真正的数组
let divs = document.querySelectorAll('div'); // 这是一个伪数组
// 将伪数组转换为真实的数组
console.log(divs);
let newDivs = [...divs];
console.log(newDivs);
2.9 Symbol
Symbol 基本使用
- ES6 引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
JavaScript 的七种数据类型
-
u undefined
。 -
s string symbol
。 -
o Object
。 -
n null number
。 -
b boolean
。
Symbol 特点
-
Symbol
的值是唯一的,用来解决命名冲突的问题。 -
Symbol
值不能与其他数据进行运算。 -
Symbol
定义 的 对象属 性 不能 使 用for…in
循 环遍 历 ,但 是可 以使用Reflect.ownKeys
来获取对象的所有键名。
// 向对象中添加属性和方法 up down
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('我可以向上');
};
game[methods.down] = function () {
console.log('我可以向下');
};
console.log(game);
/****************************************************************************************/
// 第二种方式
let obj = {
name: '張三',
up: function () {
console.log('上');
},
[Symbol('say')]: function () {
console.log('说话');
},
[Symbol('sing')]: function () {
console.log('唱歌');
}
}
console.log(obj);
注: 遇到唯一性的场景时要想到 Symbol。
Symbol 内置值
- 除了定义自己使用的
Symbol
值以外,ES6
还提供了 11 个内置的Symbol
值,指向语言内部使用的方法。 可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
名称 | 作用 |
---|---|
Symbol.hasInstance |
当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable |
对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于Array.prototype.concat() 时,是否可以展开。 |
Symbol.species |
创建衍生对象时,会使用该属性 |
Symbol.match |
当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace |
当该对象被 str.replace(myObject) 方法调用时,会返回该方法的返回值。 |
Symbol.search |
当该对象被str. search (myObject) 方法调用时,会返回该方法的返回值。 |
Symbol.split |
当该对象被 str. split (myObject) 方法调用时,会返回该方法的返回值。 |
Symbol.iterator |
对象进行for...of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器 |
Symbol.toPrimitive |
该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol. toStringTag |
在该对象上面调用toString 方法时,返回该方法的返回值 |
Symbol. unscopables |
该对象指定了使用with 关键字时,哪些属性会被with 环境排除。 |
- 使用
Symbol.isConcatSpreadable
设置数组是否可以进行拆解。
// Symbol 内置了 11 个属性
// [Symbol.hasInstance]
class Person {
static [Symbol.hasInstance](param) {
console.log(param);
console.log('我是用来检测类型的');
// 根据return 返回确定 false 或者 true
}
}
let obj = {};
console.log(obj instanceof Person);
// 数组是否拆解合并
let oldArr = [1, 2, 3, 4];
let newArr = [4, 5, 6, 7];
newArr[Symbol.isConcatSpreadable] = false; // 设置是否拆解进行连接
newArr = oldArr.concat(newArr);
console.log(newArr);
2.10 迭代器
-
遍历器(
Iterator
)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator
接口,就可以完成遍历操作。 -
ES6
创造了一种新的遍历命令for...of
循环,Iterator
接口主要供for...of
消费。 -
原生具备
iterator
接口的数据(可用for of
遍历)。
Array
Arguments
Set
Map
String
TypedArray
NodeList
工作原理
-
创建一个指针对象,指向当前数据结构的起始位置;
-
第一次调用对象的
next
方法,指针自动指向数据结构的第一个成员; -
接下来不断调用
next
方法,指针一直往后移动,直到指向最后一个成员; -
每调用
next
方法返回一个包含value
和done
属性的对象。
自定义遍历数据
// 声明一个对象
const banji = {
name: '终极一班',
stu: [
'zhansgan',
'lisi',
'wangwu',
'zhaoliu'
],
/**
*
* 重写迭代器遍历对象中的数组
* */
[Symbol.iterator]() {
let index = 0;
// 保存 this
let _this = this;
return {
next: function () {
if (index < _this.stu.length) {
// 这是迭代器中返回的结果想
let result = {value: _this.stu[index], done: false};
index++;
return result;
} else {
return {value: undefined, done: true};
}
}
}
}
};
/**
* 使用for ... of ... 遍历
*/
for (let item of banji) {
console.log(item);
}
2.11 生成器
- 生成器函数是
ES6
提供的一种异步编程解决方案
,语法行为与传统函数完全不同。
// 生成器是一个函数 是一个异步的编程解决方案
// 异步编程使用的是纯回调函数
// yield 是函数代码的分割符
function* gen() {
console.log('hello generator');
console.log('111');
yield '你是小猪猪';
console.log('222');
yield '你的小尾巴';
console.log('333');
yield '我你';
console.log('444');
}
let iterator = gen();
console.log(iterator);
// 调用生成器函数
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// console.log(iterator.next());
// 可以使用 for...of... 进行遍历
for (let item of gen()) {
console.log(item);
}
代码说明:
-
*
的位置没有限制; -
生成器函数返回的结果是迭代器对象,调用迭代器对象的
next
方法可以得到yield
语句后的值; -
yield
相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次next
方法,执行一段代码; -
next
方法可以传递实参,作为yield
语句的返回值。
生成器函数的参数
/**
* 生成器函数
* @param arg
*/
function* gen(arg) {
console.log(arg); // 666
let one = yield '1111';
console.log(one); // 第一个参数
let two = yield '2222';
console.log(two); // 第二个参数
let three = yield '3333';
console.log(three); // 第三个参数
}
// next中可以传递参数,参数将作为yield的返回值返回。
let iterator = gen('666');
iterator.next();
iterator.next('第一个参数');
iterator.next('第二个参数');
iterator.next('第三个参数');
案例 :1s 以后在控制台输出 111 , 2s之后输出 222 3s之后输出333
1s111 00_00_00-00_00_30.gif- 传统的定时器嵌套调用的方式 :容易形成回调地狱
// 下面这是回调地狱
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);
- 使用生成器函数解决回调地狱问题
/**
* 使用生成器函数解决回调地狱问题
*/
function one() {
setTimeout(() => {
console.log('444');
// 执行完成调用下一次
iterator.next();
}, 1000);
}
function two() {
setTimeout(() => {
console.log('555');
// 执行完成调用下一次
iterator.next();
}, 2000);
}
function three() {
setTimeout(() => {
console.log('666');
// 执行完成调用下一次
iterator.next();
}, 3000);
}
let iterator = gen();
/**
* 生成器函数
*/
function* gen() {
yield one();
yield two();
yield three();
}
iterator.next();
案例 :分时段(先后)请求数据 ,先请求用户数据,在请求订单数据,最后请求商品数据
异步获取数据 00_00_00-00_00_30.giffunction getUsers() {
setTimeout(() => {
let data = '用户数据';
iterator.next(data); // 通过next 将参数传递过去
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = '订单数据';
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = '商品数据';
iterator.next(data);
}, 1000);
}
/**
* 生成器函数
*/
function* gen() {
// 接收第二个next传递过来的参数
let user = yield getUsers();
console.log(user);
let order = yield getOrders();
console.log(order);
let good = yield getGoods();
console.log(good);
}
let iterator = gen();
// 第一次调用
iterator.next();
2.12 Promise
-
Promise
是 ES6 引入的异步编程的新解决方案。语法上Promise
是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果。
Promise 解决回调地狱问题
/**
* promise解决回调地狱的问题
* @type {Promise<any>}
*/
// 实例化 Promise的对象
const p = new Promise(function (resolve, reject) {
setTimeout(function () {
/*// 成功的情况
let data = '数据库中的用户数据';
// resolve 调用它代表成功
resolve(data);*/
/*************************************************************/
/*// 失败的情况*/
let error = '获取数据库数据失败';
reject(error); // reject 代表失败
}, 5000);
});
// 调用promise对象的then方法
p.then(function (value) {
// 成功
console.log(value);
}, function (reason) {
// 失败
console.error(reason);
});
Promise + Node 实现简单的文件读取
- 使用传统的方式 :
//1. 引入fs模块
const fs = require('fs'); // 需要node环境的支持
// 2. 调用方法读取文件
/**
* 读取文件内容 data中是一个· buffer
*/
fs.readFile('../resources/666.md', (error, data) => {
// 如果失败将会抛出异常
if (error) {
throw error;
}
console.log(data.toString());
});
- 使用Promise的方式 :
//1. 引入fs模块
const fs = require('fs'); // 需要node环境的支持
// 3. 使用 promise 修改读取文件的操作
const p = new Promise(function (resolve, reject) {
fs.readFile('../resources/666.md', (err, data) => {
if (err) {
// reject 是拒绝的意思
reject(err);
}
// resolve 是解决的意思
resolve(data);
})
});
p.then(function (value) {
console.log(value.toString());
}, function (reason) {
console.log(reason);
});
使用Promise发送一个Ajax请求
- 如果有一种情况是需要先发送一个请求之后再发送另一个请求,如果请求多的情况下可能出现回调地狱的情况。
const p = new Promise((resolve, reject) => {
// 1. 创建一个对象
const xhr = new XMLHttpRequest();
// 2. 初始化
xhr.open('GET', 'url');
// 3. 发送
xhr.send();
// 4. 绑定事件
xhr.onreadystatechange = function () {
// 判断
if (xhr.readyState === 4) {
// 判断状态码 处于 200 - 299 之间
if (xhr.status >= 200 && xhr.status <= 299) {
// 表示成功
// console.log(xhr.response);
// 表示成功
resolve(xhr.response);
} else {
// console.log(xhr.status);
reject(xhr.status);
}
}
};
});
/**
* 使用then处理结果,解决回调地狱的问题
*/
p.then((value) => {
console.log(value);
}, (reason) => {
console.log(reason);
})
Promise.prototype.then 调用then方法then方法的返回结果是 Promise对象,对象状态由回调函数的执行结果决定。
-
如果回调函数中返回的结果是非
Promise
类型的属性状态为成功,返回值为对象成功的值。 -
如果没有return返回结果,也是一个非
Promise
对象 ,此时Promise
类型属性状态为成功。 -
如果回调函数中返回的结果是一个
Promise
类型的对象。 -
如果抛出错误也是失败的
Promise
的错误。
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('用户数据');
}, 1000)
});
const result = p.then((value) => {
console.log(value);
// return 123; // fulfilled 是完成的意思
// 如果抛出异常
// throw new Error('发生了错误'); // rejected
// 如果回调函数中返回的是一个 Promise 对象
return new Promise((resolve, reject) => {
resolve('解决'); // fulfilled
});
}, reason => {
console.log(reason);
});
console.log(result);
Promise.prototype.catch 方法类似一个语法糖
const p = new Promise(
(resolve, reject) => {
setTimeout(() => {
reject('出错了!');
}, 1000)
}
);
/**
* catch类似一个语法糖
*/
p.catch((reason) => {
console.warn(reason);
});
2.13 Set
-
ES6
提供了新的数据结构Set
(集合) 。它类似于数组,但成员的值都是唯一的,集合实现了iterator
接口,所以可以使用『扩展运算符』和『for…of…
』进行遍历,集合的属性和方法:
size
返回集合的元素个数;add
增加一个新元素,返回当前集合;delete
删除元素,返回boolean
值;has
检测集合中是否包含某个元素,返回boolean
值;clear
清空集合,返回undefined
。
let s = new Set();
console.log(typeof s);
let s2 = new Set(['张三', '李四', '王五', '赵六']);
console.log(s2.size);// 4
// add
s2.add('田七');
console.log(s2); //
// delete
s2.delete('张三');
// has
console.log(s2.has('田七')); // true
// clear
// s2.clear();
console.log(s2);
// Set集合实现了 iterator 接口
// 打印集合
for (let item of s2) {
console.log(item); // 李四 王五 赵六 田七
}
Set 集合实践:数组去重 + 求交集、并集、差集
-
交集:两个数组中同时拥有的元素。
-
并集:在数组一中有,在数组二中也有。
-
差集:求数组一和数组二的差集。在数组一中存在,但是在数组二中不存在的元素。
let arr = [1,2,3,4,5,4,3,2,1];
// 1. 数组去重
let result = [...new Set(arr)]; // 元素唯一的数组
console.log(result); // [1,2,3,4,5]
// 2. 交集,在数组1 和数组2 中同时存在的元素
let array = [1,2,6,8,9,5,4];
// 首先将数组转换为一个集合再判断 arr 中的元素是否在 array 中存在
let publicRes = arr.filter(item => new Set(array).has(item));
// 将得到的数组通过集合去重
console.log([...new Set(publicRes)]);// 去重 [1,2,4,5]
// 3. 并集 在数组1中有在数组2中也有
let union = [...new Set([...array, ...arr])];
console.log(union); // [1,2,6,8,9,5,4,3]
// 4. 差集 arr 中存在的元素在 array 中不存在的
let diff = array.filter(item => !(new Set(arr).has(item)));
console.log(diff); // [6,8,9]
集合与数组之间的转换
let oldArr = [3,2,1,5,6,89,5];
// 数组转集合
let oldSet = new Set(oldArr);
console.log(oldSet); // {3, 2, 1, 5, 6, 89,5}
// 集合转数组
let newArr = [...oldSet];
console.log(newArr); // [3, 2, 1, 5, 6, 89]
2.14 Map
-
ES6 提供了
Map
数据结构。它类似于对象,也是键值对的集合。 但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键
。 Map 也实现了iterator
接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。 Map 的属性和方法:
size
返回Map
的元素个数;set
增加一个新元素,返回当前Map
;get
返回键名对象的键值;has
检测Map
中是否包含某个元素,返回boolean
值;clear
清空集合,返回undefined
;
// 声明Map
let m = new Map();
// 添加元素
m.set('name', '仙桃');
m.set('change', function () {
console.log('我发生了改变');
});
// 键是一个对象
let key = {
school: 'XT'
};
m.set(key, ['张三', '李四', '王五']);
// size
console.log(m.size); // 3
// 删除
m.delete('name');
console.log(m); //
// 获取
console.log(m.get(key)); // ['張三','李四','王五']
console.log(m.get('change')); // f() {console.log('我发生了改变');}
// 清空
m.clear();
console.log(m); // Map(0)
2.15 class 类
-
ES6
提供了更接近传统语言的写法,引入了Class
(类)这个概念,作为对象的模板。通过class
关键字,可以定义类。基本上, ES6 的class
可以看作只是一个语法糖,它的绝大部分功能, ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
-
class
声明类; -
constructor
定义构造函数初始化; -
extends
继承父类; -
super
调用父级构造方法; -
static
定义静态方法和属性; -
父类方法可以重写。
// 手机类
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
// 添加方法 , 使用原型对象添加实现共享
Phone.prototype.call = function () {
console.log('我可以打电话');
}
// 实例化对象
let huawei = new Phone('荣耀', 3999);
huawei.call();
console.log(huawei);
class Ps5 {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
play() {
console.log('我可以打游戏');
}
}
// 创建对象实例
let ps = new Ps5('任天堂',2999);
ps.play();
console.log(ps);
类里面的静态成员
/**
* 在ES5中*************************************************
*/
function Phone() {
}
Phone.name = '我是静态成员'; // 属于函数对象的 不属于实例对象 为静态成员
Phone.change = function () {
console.log('我可以改变世界');
}; // 属于函数对象的 不属于实例对象 为静态成员
let nokia = new Phone();
console.log(nokia.name); // undefined
Phone.prototype.size = '5.5';
console.log(nokia.size);
/**
* 在ES6中************************************************
*/
class Person {
// 在类中声明一个静态成员
static name = '王五';
age = 23;
}
console.log(Person.name);
console.log(Person.age); // undefined
let p = new Person();
console.log(p.age);
console.log(p.name); // undefined
ES5中实现继承
/**
* ES5中实现类的继承
*/
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
function OnePlus(brand, price, color, size) {
Phone.call(this, brand, price);
this.color = color;
this.size = size;
}
// 设置子级构造函数的原型
OnePlus.prototype = new Phone();
OnePlus.prototype.constructor = OnePlus;
// 声明子类方法
OnePlus.prototype.photo = function () {
console.log('我可以打电话');
}
OnePlus.prototype.playGame = function () {
console.log('我可以玩游戏');
}
// 创建onPlus 实例
let onePlus = new OnePlus('1+', 2999, '红色', 5.5);
console.log(onePlus);
onePlus.photo();
onePlus.playGame();
// 原型对象 和 对象原型
console.log(OnePlus.prototype);
console.log(onePlus);
ES6 中类的继承
- 也可以对继承的方法进行重写,
但是在重写的方法中不能通过
super关键字调用父类的该方法。
class Phone {
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
call() {
console.log('我可以打电话');
}
}
class OnePlus extends Phone {
/**
* 构造函数
*/
constructor(brand, price, color, size) {
super(brand, price); // Phone(this,brand,price);
this.color = color;
this.size = size;
}
photo() {
console.log('拍照');
}
playGame() {
console.log('玩游戏');
}
}
let onePLus = new OnePlus('1+', 3999, '绿色', 6.6);
onePLus.photo(); // 拍照
onePLus.playGame(); // 玩游戏
onePLus.call(); // 我可以打电话
setter 和 getter 方法
// get 和 set
class Phone {
get price() {
console.log('获取价格');
return '2999';
}
set price(newValue) {
console.log('价格被修改了');
}
}
let s = new Phone();
console.log(s.price);
s.price = '3999';
console.log(s.price);
2.16 ES6中数值的扩展
二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
Number.isFinite() 与 Number.isNaN()
Number.isFinite()
用来检查一个数值是否为有限的;
Number.isNaN()
用来检查一个值是否为NaN
。
Number.parseInt() 与 Number.parseFloat()
ES6
将全局方法parseInt 和 parseFloat
,移植到Number
对象上面,使用不变。
Math.trunc
- 用于去除一个数的小数部分,返回整数部分。
Math.sign
- 判断一个数到底是 正数 还是负数 还是零。正数返回 1 ,负数返回 -1 零返回 0
代码演示
// 1. Number.EPSILON 是JavaScript表示的最小精度
console.log((0.1 + 0.2)); // 0.30000000000000004
// 2. 二进制和八进制
// 二进制
let b = 0b0101;
console.log(b); // 5
// 八进制
let o = 0o777;
console.log(o); // 511
// 十进制
let d = 10106;
console.log(d); // 10106
// 十六进制
let x = 0xfff;
console.log(x);// 4095
// 3. Number.isFinite() 检测一个数值是否是一个有限数
console.log(Number.isFinite(10)); // true
console.log(Number.isFinite(100 / 0)); // false
console.log(Number.isFinite(Infinity)); // false
// 4. Number.isNaN() 检测一个数值是否为NaN
console.log(Number.isNaN(123));// false
console.log(Number.isNaN(Number.parseInt('kk456')));// true
// 5. Number.parseInt Number.parseFloat字符串转整数或者浮点数
let str = '123456';
console.log(str); // 黑色字符串
let num = Number.parseFloat(str);
console.log(num);// 蓝色数字
str = 'kk456';
num = Number.parseInt(str);
console.log(num);
// 6. Number.isInteger 判断一个数是否为整数
let intNum = 123;
console.log(Number.isInteger(intNum)); // true
// 7. Math.trunc() 将数字的小数部分抹掉
let floatNum = 123.365;
console.log(Math.trunc(floatNum)); // 123
// 8. Math.sign 判断一个数到底是 正数 还是负数 还是零
console.log(Math.sign(10)); // 1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-10)); // -1
Number.isInteger
- Number.isInteger() 用来判断一个数值是否为整数。
2.17 对象扩展
ES6 新增了一些 Object
对象的方法
-
Object.is
比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN); 在其他情况下的比较情况下NaN
和谁比较都是false
。但是在这里NaN
和NaN
比较就是true
。 -
Object.assign
对象的合并,将源对象的所有可枚举属性,复制到目标对象; -
__proto__
、setPrototypeOf
、setPrototypeOf
可以直接设置对象的原型。
// 1. Object.is 判断两个值是否完全相等
console.log(Object.is(120, 120)); // true
console.log(Object.is(NaN, NaN)); // true
console.log((NaN === NaN));// false
// NaN 不管和谁使用 === 做比较都是false
// 2.Object.assign 对象合并
const configA = {
driver: 'jdbc.mysql.DataManagerDriver',
url: 'localhost',
username: 'root',
password: 'root',
test: 'test'
};
const configB = {
driver: 'jdbc.mysql.DataManagerDriver',
url: '127.0.0.1',
username: 'admin',
password: '12345'
}
console.log(Object.assign(configA, configB));
console.log(configA);// 已经合并
console.log(Object.assign(configB, configA));
// 3. 设置原型对象 Object.setPrototypeOf = 设置原型对象
const school = {
school: '仙桃大数据学院'
};
const area = {
area: '渝北'
};
Object.setPrototypeOf(school, area);
console.log(school);
// 4. 获取原型 Object.getPrototypeOf = 获取原型对象
console.log(Object.getPrototypeOf(school));
2.18 模块化
- 模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
模块化的好处
-
防止命名冲突;
-
代码复用;
-
高维护性。
模块化规范产品
ES6 之前的模块化规范有:
-
CommonJS => NodeJS、 Browserify
; -
AMD => requireJS
; -
CMD => seaJS
。
ES6 模块化语法
模块功能主要由两个命令构成: export
和 import
。
-
export
命令用于规定模块的对外接口; -
import
命令用于输入其他模块提供的功能。
export的三种暴露方式
(一) export 分别暴露的方式
/**
* 下面的属于分别暴露
* @type {string}
*/
export let school = '仙桃大数据学院';
export function teach() {
console.log('传授大数据开发经验');
}
(二)export统一暴露的方式
/**
* 统一暴露
* @type {string}
*/
let school = '仙桃大数据学校';
function stuData() {
console.log('学习大数据');
}
/**
* 统一暴露
*/
export {
school,
stuData
}
(三)export 默认暴露的方式
export default {
school:'仙桃大数据学院',
change:function () {
console.log('为技术而改变');
}
}
import 的三种引入方式
(一) 使用解构赋值的方式引入
// m1
import {school,teach} from '../js/m1.js';
console.log(school);
teach();
(二) 如果变量中出现了变量名重复的情况使用as进行重命名
// m2
import {school as xt,stuData} from '../js/m2.js';
console.log(xt);
stuData();
(三) 导入默认暴露方式有两种方式
// m3 导入默认暴露
import {default as m3} from '../js/m3.js';
console.log(m3);
// 简写形式只针对默认暴露
import m3Default from '../js/m3.js';
console.log(m3Default);
m3Default.change();
浏览器中使用模块化的方式
- 将模块的引入统一写到一个JS文件中
app.js
。
// 入口文件
// 模块引入
import * as m1 from '../js/m1.js';
import * as m2 from '../js/m2.js';
import * as m3 from '../js/m3.js';
- 在页面文件引入,
app.js
并将script
的类型设置为module
。
<!--类型需要设置成module-->
<script src="../js/app.js" type="module"></script>
ES6 中使用babel对ES6模块化代码进行转换使其通用
步骤
-
安装工具:
babel-cli
、babel-preset-env
(预设包)、browserify (webpack)
(打包工具)。 -
使用该安装命令 :
npm i babel-cli babel-preset-env browserify -D
进行安装。 -
执行
babel
命令(非全局安装使用npx):npx babel js -d dist/js --presets=babel-preset-env
。 -
打包:
npx browserify dist/js/app.js -o dist/bundle.js
。
ES6模块化规范和NPM包的使用
- 使用
npm
命令安装jquery
:npm i jquery
;
3. ES7 新特性
3.1 Array.prototype.includes
-
Includes
方法用来检测数组中是否包含某个元素,返回布尔类型值。和原来数组中的indexOf()
方法进行区分。indexOf()
返回的值是元素的位置,而includes
方法返回的是true
或者false
。
// includes
const four = ['西游记','红楼梦','三国演义','水浒传'];
// 判断
console.log(four.includes('西游记')); // true
console.log(four.includes('金画画')); // false
// indexOf
console.log(four.indexOf('西游记')); // 0
console.log(four.indexOf('水浒传')); // 3
console.log(four.indexOf('金画画')); // -1
3.2 指数操作符(幂运算)
- 在
ES7
中引入指数运算符「**」
,用来实现幂运算,功能与Math.pow
结果相同如210。
// 幂运算 **
console.log((2 ** 10)); // 1024
console.log(Math.pow(2, 10)); // 1024
4. ECMASript 8 新特性
4.1 async 和 await
-
async
和await
两种语法结合可以让异步代码像同步代码一样。
async 函数
-
async
函数的返回值为promise
对象; -
promise
对象的结果由async
函数执行的返回值决定。
/**
* async(异步的) 修饰的函数返回的是一个 promise 对象
* 1. 返回的结果不是一个Promise类型的对象 就是一个成功的Promise;
* 2. 抛出错误 返回的结果是一个失败的Promise;
* 3. 如果返回的结果是一个Promise对象 ;
*/
async function fun() {
// 1. 返回的结果不是一个Promise对象
// return '我是字符串';
// 2. 返回的结果中抛出异常
// throw new Error('这是一个错误!');
// 3. 返回的结果是一个Promise对象
return new Promise((resolve, reject) => {
// resolve('成功!');
reject('拒绝!');
})
}
const result = fun();
console.log(result);
/**
* 处理结果
*/
result.then(
value => {
console.log(value);
},
reason => {
console.log(reason);
});
4.2 await 表达式
-
await
必须写在async
函数中; -
await
右侧的表达式一般为promise
对象; -
await
返回的是promise
成功的值; -
await
的promise
失败了, 就会抛出异常, 需要通过try...catch
捕获处理。
const p = new Promise((resolve, reject) => {
// resolve('成功的值!');
reject('失败了!'); // 如果这里失败 在async 中的await需要使用 try...catch() 捕获
});
/**
* await 需要放在 async 函数中
*/
async function mainFun() {
try {
let result = await p;
console.log(result);
} catch (e) {
console.log(e); // 失败了! 是这里打印的哦
}
}
// 调用函数
mainFun();
async 和 await 读取多个文件
-
需要node环境的支持。
-
运行时只需要在命令行
node 文件名.js
即可。
// 引入fs模块
const fs = require("fs");
// 读取文件
/**
* 读取文件 666.md
*/
function rf666() {
return new Promise((resolve, reject) => {
fs.readFile('../resources/666.md', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
function rf888() {
return new Promise((resolve, reject) => {
fs.readFile('../resources/888.md', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
function rf999() {
return new Promise((resolve, reject) => {
fs.readFile('../resources/999.md', (err, data) => {
if (err) reject(err);
resolve(data);
});
});
}
async function main() {
try {
let f666 = await rf666();
let f888 = await rf888();
let f999 = await rf999();
console.log(f666.toString());
console.log(f888.toString());
console.log(f999.toString());
} catch (e) {
console.log(e);
}
}
main();
async 和 await 封装ajax请求
/**
* 发送ajax请求返回的结果是一个 Promise 对象
*/
function sendAjax(url) {
return new Promise((resolve, reject) => {
// 1. 创建对象
const xhr = new XMLHttpRequest();
// 2. 初始化
xhr.open('GET', url);
// 3. 发送请求
xhr.send();
// 4. 绑定事件
xhr.onreadystatechange = function () {
// 判断
if (xhr.readyState === 4) { // 需要加上这句状态的判断否则请求不到数据
// 判断状态码 处于 200 - 299 之间
if (xhr.status >= 200 && xhr.status <= 299) {
// 表示成功
// console.log(xhr.response);
// 表示成功
resolve(xhr.response);
} else {
// console.log(xhr.status);
reject(xhr.status);
}
}
};
});
}
console.log(sendAjax('https://api.apiopen.top/getJoke'));
/**
* 使用then处理
*/
sendAjax('https://api.apiopen.top/getJoke').then(value => {
// console.log(value);
}, reason => {
console.log(reason);
});
// 使用 async 和 await 进行处理
async function main() {
let joke = await sendAjax('https://api.apiopen.top/getJoke');
console.log(joke);
}
main();
4.2 Object.values 和 Object.entries 、Object.keys()对象方法扩展
Object.values()
方法返回一个给定对象的所有可枚举属性值的数组;
Object.entries()
方法返回一个给定对象自身可遍历属性[key,value]
的数组;
Object.keys()
方法返回一个给定对象的所有可枚举的属性。
4.3 Object.getOwnPropertyDescriptors
该方法返回指定对象所有自身属性的描述对象
5. ECMASript 9 新特性
5.1 Rest/Spread 属性
-
Rest
参数与spread
扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组,
在ES9 中为对象
提供了像数组一样的 rest 参数和扩展运算符
/**
* 对象的扩展运算符
*/
const connectCfg = {
host: '127.0.0.1',
port: '3306',
username: 'root',
password: 'root',
type: 'db'
}
/**
* 对象的展开
* @param host
* @param port
* @param user
*/
function connect({host, port, ...user}) {
console.log(host);
console.log(port);
console.log(user);
}
connect(connectCfg);
const skillOne = {
q: '天音波'
};
const skillTwo = {
w: '金钟罩'
};
const skillThree = {
e: '天雷破'
};
const skillFour = {
r: '猛龙摆尾'
};
// 将对象拆分为 数组
const ms = {...skillOne, ...skillTwo, ...skillThree, ...skillFour};
console.log(ms);
5.2 正则扩展-命名捕获分组
- ES9 允许命名捕获组使用符号『?<name>』 ,这样获取捕获结果可读性更强。
// 声明一个字符串
let str = '<a href="https://www.baidu.com">百度一下</a>'
// 编写正则
let reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result);
console.log(result.groups.url);
console.log(result.groups.text);
5.3 正则扩展-反向断言
- ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
/**
* 1. 正向断言
*
* 2. 反向断言
*
*/
// 正向断言
let str = 'zhangsan1235656哈哈哈哈'
const reg = /\d+(?=哈)/;
const result = reg.exec(str);
console.log(result);
// 反向断言 : 可以根据前边的内容做判断
const rg = /(?<=n)\d+/;
console.log(rg.exec(str));
5.4 正则扩展dot-All
- 正则表达式中点.匹配除回车外的任何单字符,标记『s』 改变这种行为,允许行终止符出现。
let str =
'<ul>
<li>
<a>肖申克的救赎</a>
<p>上映时间:2019</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映时间:2018</p>
</li>
</ul>';
/**
* dot . 元字符 除换行符以外的任意单个字符
*
* 多了一个模式修正符 s
*
* g 全局修饰符
*/
let reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs;
let result;
let data = [];
while (result = reg.exec(str)) {
data.push({title: result[1], time: result[2]});
}
console.log(data);
6. ES10 新特性
6.1 Object.fromEntries 和 Object.entries
Object.fromEntries
将数组转换为对象。
Object.entries
将对象转换为数组。
// 二维数组
const result = Object.fromEntries([
['name', '仙桃大数据学院'],
['subject', 'Java、前端']
]);
let map = new Map();
map = Object.fromEntries(result);
console.log(map);
6.1 trimEnd() 和 trimStart()
trimEnd()
清除尾部的空白。
trimStart()
清除首部的空白。
6.2 flat 和 flatMap
flat
将多维数组转换为低维数组 ,参数传递的是代表 深度。
flatMap
类似于数组的map
方法。map()方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。参考网址