第 1章、 ECMASript 相关介绍
1.1什么是ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该组织改名为 Ecma 国际。
1.2 什么是 ECMAScript
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。
1.3 什么是 ECMA-262
Ecma 国际制定了许多标准,而 ECMA-262 只是其中的一个,所有标准列表查看
www.ecma-international.org/publication…
1.4ECMA-262历史
ECMA-262(ECMAScript)历史版本查看网址
www.ecma-international.org/publication…
第 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 兼容性
kangax.github.io/compat-tabl… 可查看兼容性
第二章、ECMASript6的新特性
2.1 let关键字
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
- 不允许重复声明
let a = "张三";
let a = "男" // 会报错 Identifier 'a' has already been declared
- 块儿级作用域(if、else、while、 for在这些中都会有块级作用域)
if(true){
// let a = "张三";
var a = "张三";
}
console.log(a); // 报错 a is not defined
console.log(a); //张三 var没有块级作用域
- 不存在变量提升
console.log(a);
let a = "张三"; //会报错 Cannot access 'a' before initialization
console.log(b); //undefined var会变量提升
var b = "李四";
- 不影响作用域链
function fun(){
let a = "张三";
function fun1(){
console.log(a);
}
fun1()
}
fun() //张三
应用场景:以后声明变量用let就对了
2.1.1 点击切换颜色
//需要给div添加样式
<div class="yhl">
<h2 class="ll">点击切换颜色</h2>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
<script>
// 获取元素
let items = document.getElementsByClassName("item");
// 循环遍历
for (let i = 0; i < items.length; i++) {
//绑定点击事件
items[i].onclick = function () {
console.log(i);
// 如果用var下面这样写就不行 因为i = 3 我们就定义三个索引最大为2 所以会报错
// 为什么会变成3呢 因为var i是一个全局变量 i一直在自增 所以i出循环以后就是3了
// 怎么解决 把for循环的var换成let 就可以完美解决了
items[i].style.background = 'pink';
}
}
</script>
2.2.const关键字
const 关键字用来声明常量,const 声明有以下特点
- 声明必须赋初始值
// 声明必须赋初始值
const A; //会报错,所以声明变量的时候要复制
- 标识符一般为大写(潜规则)
// 标识符一般为大写(潜规则)
const a = 10;
console.log(a); //10
- 不允许重复声明
// 不允许重复声明
const A = "张三";
const A = "李四"; //报错
- 值不允许修改
// 值不允许修改
const A = "张三";
A = "李四"; //报错 常量不能改
- 块儿级作用域
// 块级作用域
if(true){
let A = "张三";
}
console.log(A); //报错
注意 : 对象属性修改和数组元素变化不会出发 const 错误
//对象数组可以修改
const A = [1,2,3,4];
A.push(5)
console.log(A); //[1,2,3,4,5]
常量的值不是不可以改变吗,这个为什么可以改?
因为数组和对象的所指向的地址没有改变,所以不会报错
应用场景:声明对象类型使用 const,非对象类型声明选择 let
2.3变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
数组的解构赋值
// 数组的变量解构赋值
let arr = ["赵本山", "小沈阳", "丫蛋", "刘能"];
let [zhao, xiao, ya, liu] = arr;
console.log(zhao); // 赵本山
console.log(xiao); // 小沈阳
console.log(ya); // 丫蛋
console.log(liu); // 刘能
简单对象的解构赋值
const obj = {
name: "赵本山",
age: 80,
say: function () {
console.log("我可以演小品");
}
}
const { name, age, say } = obj;
console.log(name); //赵本山
console.log(age); // 80
console.log(say); //function(){console.log("我可以演小品")}
复杂对象的解构赋值
let obj = {
name:"张三",
age:18,
food:['土豆','西红柿','茄子','芹菜'],
girlFriend:[
{name:"小红"},
{name:"小粉"},
{name:"小兰"}
]
}
let {name,age,food:[one,two,three,four],girlFriend:[first,second,thrid]} = obj;
console.log(name); //张三
console.log(thrid); //{name:"小兰"}
console.log(one); // 土豆
注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式
2.4模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符
let a = `
<ul>
<li>赵本山</li>
<li>小沈阳</li>
<li>丫蛋</li></li>
<li>刘能</li>
</ul>
`;
console.log(a);
// 输出结果
/*<ul>
<li>赵本山</li>
<li>小沈阳</li>
<li>丫蛋</li></li>
<li>刘能</li>
</ul>*/
- 可以使用 ${xxx} 形式输出变量
let stu = "张三";
let str = `${stu}明天就可以不上课了`;
console.log(str); //张三明天就可以不上课了
< 注意:当遇见字符串与变量拼接的情况下使用模板字符串
2.5简化对象的写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。(kv一直省略v)
let name = "张三";
let fun = function(){
console.log('我明天要上课');
}
let obj = {
name,
fun,
stu(){
console.log('明天是周六');
}
}
console.log(obj); //{name: '张三', fun: ƒ}
console.log(obj); //{name: '张三', fun: ƒ, stu: ƒ}
注意:对象简写形式简化了代码,所以以后用简写就对了
2.6箭头函数
ES6 允许使用「箭头」(=>)定义函数。
let sum = (a,b,c)=>{
console.log(a+b+c);
}
sum(1,2,3) //6
箭头函数的注意点:
- 如果形参只有一个,则小括号可以省略
let fun = a=>{
console.log(a+a);
}
fun(2); //4
- 函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的执行结果
let fun = a => a * a;
let result = fun(3);
console.log(result); //9
- 箭头函数 this 指向声明时所在作用域下 this 的值(this指向是静态的)
let name = "张三";
function fun(){
console.log(this.name);
};
let stu = ()=>{
console.log(this.name);
};
fun();//张三
stu();//张三
const obj = {
name:"孙悟空",
age:18
}
fun.call(obj); //孙悟空
stu.call(obj); //张三
- 箭头函数不能作为构造函数实例化
// 普通函数
let Person = function(name,age) {
this.name = name;
this.age = age;
}
let me = new Person("孙悟空",18);
console.log(me); //Person {name: '孙悟空', age: 18}
// 箭头函数
let Stu = (name,age)=>{
this.name = name;
this.age = age;
}
let you = new Stu();
console.log(you); //报错 Stu不是构造函数
- 不能使用 arguments
//普通函数
function name() {
console.log(arguments);
}
name(1,2,3); //Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 箭头函数
let name1 = ()=>{
console.log(arguments);
}
name1(1,2,3); // 报错 arguments is not defined
注意:箭头函数不会更改 this 指向,用来指定回调函数会非常合
所以箭头函数适合与this无关的回调,定时器,数组方法的回调
2.6.1 点击div 2s后变色
点击以后变成粉红色
//给div添加一个高度 宽度 背景色
<div id="ad"> </div>
<script>
// 获取元素
let ad = document.getElementById('ad');
// 绑定事件
ad.addEventListener("click",function () {
let _this = this;
console.log(_this);
// 定时器
setTimeout( ()=>{
this.style.background = 'pink'
},2000)
})
</script>
2.6.2 数组返回偶数元素
// 普通函数写法
let arr = [1, 2, 4, 6, 9, 11];
let a = arr.filter(function (item) {
// console.log(item);
if (item % 2 == 0) {
return true;
} else {
return false;
}
})
console.log(a) //[2, 4, 6]
// 箭头函数写法
let b = arr.filter(item=> item % 2 == 0)
console.log(b); //[2, 4, 6]
补充:函数参数的默认值设置
1.形参的初始值
形参的初始值,具有默认值的参数,一般位置要靠后(潜规则), 形参有默认值,如果传递的有实参就按照实参传递进去的,如果没有就按照形参的默认值
function fun(a,b,c= 10){
console.log(a+b+c);
}
fun(1,2) //13
fun(1,2,3) //6
2.与解构赋值结合
function stu ({name ='李四',age,gender,adress}){
console.log(name); //张三 如果不传name 就是李四
console.log(age); //18
console.log(gender); //男
console.log(adress); //河南
};
stu({
// name:"张三",
age:18,
gender:"男",
adress:"河南"
});
2.7rest参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
// rest参数
function fun(...args) {
console.log(args);
}
fun(1, 2, 3) // [1, 2, 3]
// rest 参数必须是最后一个形参
function fun1(a, b, ...args) {
console.log(a, b, args);
}
fun1(100, 1, 2, 3, 4, 5, 19);// 100 1 [2, 3, 4, 5, 19]
注意:rest 参数非常适合不定个数参数函数的场景
2.8spread 扩展运算符
扩展运算符(spread)也是三个点(...)。它好比 rest 参数的逆运算,将一转为用逗号分隔的参数序列,对数组进行解包。
let arr = ['张三','李四','王五','刘六'];
function fun(){
// console.log(arguments); //['张三', '李四', '王五', '刘六']是一个数组
console.log(arguments); //变成了4个,分开的
}
fun(...arr);
2.8.1 合并数组
let a = [1,2];
let b = [3,4];
// ES5语法
// let arr = a.concat(b); [1, 2, 3, 4]
// 扩展运算符
let arr = [...a,...b]; //[1, 2, 3, 4]
console.log(arr);
2.8.2 使用扩展运算符进行克隆
let san = ['E','G','M'];
let si = [...san];
console.log(si); // ['E', 'G', 'M']
2.9 Symbol
2.9.1 Symbol的基本使用
ES6 引入了一种新的原始数据类型 Symbol, 表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
// 创建Symbol值
let a = Symbol();
console.log(a); //Symbol
// 添加表示Symbol
let b = Symbol('张三');
let c = Symbol('张三');
console.log(b); //Symbol(张三)
console.log(c); //Symbol(张三)
console.log(b == c); //false
// 使用Symbol for创建 获得唯一symbol值
let d = Symbol.for('李四');
let e = Symbol.for('李四');
console.log(d); //Symbol(李四)
console.log(e); //Symbol(李四)
// console.log(d == e); //true
- Symbol 值不能与其他数据进行运算
let a = Symbol();
// let b = a+a; //报错
b = a>100 //报错
console.log(b);
- Symbol 定义的对象属性不能使用 for…in 循环遍历, 但是可以使用Reflect.ownKeys 来获取对象的所有键名
2.9.2给对象添加放方法
可以给对象添加属性属性和方法,如果不知道原来的对象有没有这个方法和属性,可以使用Symbol来进行更加安全的添加属性和方法。
// Symbol的使用 第一无二的值,给对象添加属性和方法
const obj ={
name:"孙悟空",
up:function(){
console.log("我是up");
},
down:function(){
console.log("我的dowm");
}
}
// 声明一个对象
const a = {
up:Symbol(),
down:Symbol()
}
// 这样添加一个值更加安全
obj[a.up] = function (){
console.log("我是symbol里的up");
}
obj[a.down] = function (){
console.log("我是symbol里的down");
}
console.log(obj); //name: '孙悟空', up: ƒ, down: ƒ, Symbol(): ƒ, Symbol(): ƒ}
2.10 迭代器
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口(js对象的属性),就可以完成遍历操作。
-
ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费
-
原生具备 iterator 接口的数据(可用 for of 遍历)
a) Array
b) Arguments
c) Set
d) Map
e) String
f) TypedArray
g) NodeList
let arr = ["唐僧","孙悟空","猪八戒","沙僧"];
for(let i of arr){
console.log(i); //唐僧,孙悟空 猪八戒 沙僧
}
// 创建一个指针对象,指向当前数据结构的起始位置
let iterator = arr[Symbol.iterator]();
//第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
console.log(iterator.next()); //{value: '唐僧', done: false}
//接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
console.log(iterator.next()); //{value: '孙悟空', done: false}
console.log(iterator.next()); //{value: '猪八戒', done: false}
console.log(iterator.next()); //{value: '沙僧', done: false}
// 每调用 next 方法返回一个包含 value 和done 属性
// done : 表示是结束
console.log(iterator.next()); //{value: undefined, done: true}
- 工作原理
a) 创建一个指针对象,指向当前数据结构的起始位置
b) 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
c) 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员对象
d) 每调用 next 方法返回一个包含 value 和done 属性的对象注 : 需要自定义遍历数据的时候,要想到迭代器。
注 : 需要自定义遍历数据的时候,要想到迭代器。
2.2.1迭代器的使用
可以根据自己来定义,原理可以看迭代器的工作原理
const obj = {
name:"xiyou",
stus:[
'唐僧',
'孙悟空',
'猪八戒',
'沙僧'
],
[Symbol.iterator](){
// 索引变量
let index = 0;
return {
next:()=>{
if(index < this.stus.length){
const result = {value:this.stus[index],done:false};
// 下标自增
index++;
// 返回结果
return result;
}else{
return {value:undefined,done:true};
}
}
}
}
}
for(let i of obj){
console.log(i); //唐僧 孙悟空 猪八戒 沙僧
}
2.11生成器
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
代码说明:
function* gen() {
yield '一只没有耳朵';
yield '一只没有尾巴';
return '真奇怪';
}
let iterator = gen();
console.log(iterator.next()); // {value: '一只没有耳朵', done: false}
console.log(iterator.next());//{value: '一只没有尾巴', done: false}
console.log(iterator.next());//{value: '真奇怪', done: true}
console.log(iterator.next());//{value: undefined, done: true}
-
- 的位置没有限制(可以在左边,也可以在右边,也可以在中间)
-
生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到yield 语句后的值
-
yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next方法,执行一段代码
-
next 方法可以传递实参,作为 yield 语句的返回值
2.11.1生成器函数的参数
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');
// 执行第一个next()执行第一句代码
console.log(iterator.next()); //aaa
// next可以传入实参 传入的实参 会成为 yield的返回值
console.log(iterator.next('bbb')); //bbb
console.log(iterator.next('ccc')); //ccc
console.log(iterator.next('ddd')); //ddd
2.11.2 案例1
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();
2.11.3案例2
模拟获取 用户数据 订单数据 商品数据
function getUsers() {
setTimeout(()=>{
let data ='用户数据';
iterator.next(data);
},1000)
}
function getOrders(){
setTimeout(()=>{
let data = '订单数据';
iterator.next(data);
},1000)
}
function getGoods(){
setTimeout(()=>{
let data = '商品数据'
iterator.next(data);
},1000)
}
function * gen(){
let users = yield getUsers();
console.log(users);
let orders = yield getOrders();
console.log(orders);
let goods = yield getGoods();
console.log(goods);
}
let iterator = gen();
iterator.next();
2.12 Promise
Promise 是ES6 引入的异步编程的新解决方案。语法上 Promise 是一个构造函数,
用来封装异步操作并可以获取其成功或失败的结果。
// 实例化 Promise对象
let a = new Promise(function (resolve, reject) {
setTimeout(function () {
// let a = "你好";
// resolve(a)
// reject(a);
let err = "没有获取数据";
reject(err);
}, 1000)
});
// 调用Promise 对象的then方法
a.then(function (value) {
console.log(value);//你好
}, function (reason) {
console.log(reason); // 没有获取数据
});
-
Promise 构造函数: Promise (excutor) {}
-
Promise.prototype.then 方法
调用then方法 then方法返回结果是Promise对象,对象状态由回调函数执行结果决定
-
如果回调函数中返回的结果是非 Promise类型的属性,状态为成功,返回值就是对象的成功的值
let a = new Promise((resolve, reject) => { setTimeout( ()=> { resolve('用户成功的数据') }, 1000) }); // 调用Promise 对象的then方法 let yhl = a.then( value => {console.log(value); return 123;}, reason => console.log(reason) ); console.log(yhl); //Promise是成功的 值是123
-
如果回调函数的返回结果是Promise类型的属性,根据内部promsie决定then promise返回的状态
let a = new Promise((resolve, reject) => { setTimeout(() => { resolve('用户成功的数据') }, 1000) }); // 调用Promise 对象的then方法 let yhl = a.then(value => { console.log(value); return new Promise((resolve,reject) =>{ reject('ok') }); }, reason => console.log(reason) ); console.log(yhl); //Promise失败的 值是OK
-
抛出错误 失败的 错误的值就是抛出去的值
let a = new Promise((resolve, reject) => { setTimeout(() => { resolve('用户成功的数据') }, 1000) }); // 调用Promise 对象的then方法 let yhl = a.then(value => { console.log(value); throw new Error('失败')}, reason => console.log(reason) ); console.log(yhl); //失败 值就是抛出去的值
- Promise.prototype.catch 方法
2.12.1Promise封装node读取文件
// 第一步 引入fs模块
const fs = require('fs');
// 调用方法
fs.readFile('./练习.txt', (err, data) => {
if (err) throw err;
// console.log(data.toString());
});
// 使用Promsie
const a = new Promise(function (resolve, reject) {
fs.readFile('./练习.txt', function (err, data) {
if (err) reject(err);
resolve(data);
})
});
a.then(function (value) { console.log(value.toString()); },
function (reason) { console.log('读取失败'); })
文件
//练习
春晓
春眠不觉晓,
处处闻啼鸟,
夜来风雨声,
花落知多少。
//练习2
相思
红豆生南国,
春来发几枝。
愿君多采撷,
此物最相思。
//练习3
江雪
千山鸟飞绝,
万径人踪灭。
孤舟蓑笠翁,
独钓寒江雪。
2.12.2Promise封装 AJAX
原生写法
// 原生写法
// 第一步 创建一个对象
const xhr = new XMLHttpRequest();
// 第二步 初始化
xhr.open("GET","http://127.0.0.1:3000");
// 发送
xhr.send();
// 绑定事件 处理响应结果
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
// 判断状态码200-299
if(xhr.status >= 200 && xhr.status < 300){
// 表示成功
console.log(xhr.response); //这是get请求
}else{
console.log(xhr.status);
}
}
}
服务器代码
//需要安装 express
const express = require('express');
const app = express();
app.get('/',(req,res)=>{
// response.send(123)
res.setHeader("Access-Control-Allow-Origin","*")
res.send("这是get请求");
})
app.listen('3000',()=>{
console.log("启动");
})
使用Promise进行封装
const yhl = new Promise(function (resolve, reject) {
const xhr = new XMLHttpRequest();
// 第二步 初始化
xhr.open("GET", "http://127.0.0.1:3000");
// 发送
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断状态码200-299
if (xhr.status >= 200 && xhr.status < 300) {
// 表示成功
resolve(xhr.response)
} else {
reject(xhr.status);
}
}
}
});
yhl.then(function (value) { console.log(value); },
function (reason) { console.log("promise错误"); })
2.12.3 then链式案例
//原生写法
// 引入fs
const fs = require('fs');
fs.readFile('./练习.txt', (err, data1) => {
fs.readFile('./练习2.txt', (err, data2) => {
fs.readFile('./练习3.txt', (err, data3) => {
let result = data1 + '\r\n' + data2 + '\r\n' + data3;
console.log(result);
});
});
});
//运行结果就是:三首诗,顺序被读取
promise写法
// 引入fs
const fs = require('fs');
const yhl = new Promise((resolve, reject) => {
fs.readFile('./练习.txt', (err, data) => {
resolve(data)
})
});
yhl.then(value => {
return new Promise((resolve, reject) => {
fs.readFile('./练习2.txt', (err, data) => {
resolve([value, data])
})
})
}).then(
value => {
return new Promise((resolve, reject) => {
fs.readFile('./练习3.txt', (err, data) => {
value.push(data);
resolve(value)
})
})
}
).then(value => console.log(value.toString()))
//运行结果就是:三首诗,顺序被读取
文件
//练习
春晓
春眠不觉晓,
处处闻啼鸟,
夜来风雨声,
花落知多少。
//练习2
相思
红豆生南国,
春来发几枝。
愿君多采撷,
此物最相思。
//练习3
江雪
千山鸟飞绝,
万径人踪灭。
孤舟蓑笠翁,
独钓寒江雪。
2.12.4 封装sendAJAX
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
// 第二步 初始化
xhr.open("GET", url);
// 发送
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断状态码200 - 299
if (xhr.status >= 200 && xhr.status < 300) {
// 表示成功
resolve(xhr.response)
} else {
reject(xhr.status);
}
}
}
})
}
// 使用then方法
sendAJAX("http://127.0.0.1:3000").then(value => {console.log(value);}) //这是get请求
// 使用async await
async function fun(){
let result = await sendAJAX("http://127.0.0.1:3000");
console.log(result); //这是get请求
}
fun();
服务器代码
//需要安装 express
const express = require('express');
const app = express();
app.get('/',(req,res)=>{
// response.send(123)
res.setHeader("Access-Control-Allow-Origin","*")
res.send("这是get请求");
})
app.listen('3000',()=>{
console.log("启动");
})
2.13 Set
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历,集合的属性和方法:
-
size 返回集合的元素个数
-
add 增加一个新元素,返回当前集合
-
delete 删除元素,返回 boolean 值
-
has 检测集合中是否包含某个元素,返回 boolean 值
-
clear 清空集合,返回 undefined
// 声明一个Set
// let a = new Set();
// console.log(a); //Set(0) {size: 0}
// 初始参数,可迭代数据 会自动去重
let a = new Set(['孙悟空','猪八戒','沙和尚','唐僧','孙悟空']);
console.log(a); //Set(4) {'孙悟空', '猪八戒', '沙和尚', '唐僧'} 返回的是一个类数组对象
// 元素个数
console.log(a.size); //4
// 添加一个元素
let b = a.add('小白龙');
console.log(b); //{'孙悟空', '猪八戒', '沙和尚', '唐僧', '小白龙'}
// 删除一个元素 返回boolean值
let c = a.delete('沙和尚');
console.log(c); //true
// 检测是否一个值 返回是一个boolean
let s = a.has('孙悟空')
console.log(s); // true
let d = a.has('白骨精');
console.log(d); //false
// 清除集合
let e = a.clear();
console.log(e); //undefined
// 使用for of 遍历
for(let v of a){
console.log(v); // 孙悟空 猪八戒 沙和尚 唐僧 小白龙
}
2.13.1 集合实践
数组去重
// 数组去重
let arr = [1, 2, 3, 4, 3, 1];
let a = [...new Set(arr)]; //返回一个类数组对象
console.log(a); // [1, 2, 3, 4]
交集
// 交集
//基础写法
let arr = [1, 2, 3, 4, 3, 1];
let arr2 = [2, 3, 4, 5, 2];
let result = [...new Set(arr)].filter(item => {
let b = new Set(arr2);
if (b.has(item)) {
return true;
} else {
return false;
}
})
console.log(result); //[2, 3, 4]
// 简化写法
let yhl = [...new Set(arr)].filter( item => new Set(arr2).has(item));
console.log(yhl); // [2, 3, 4]
并集
// 并集
let arr = [1, 2, 3, 4, 3, 1];
let arr2 = [2, 3, 4, 5, 2];
let result = [...new Set([...arr,...arr2])];
console.log(result); //[1, 2, 3, 4, 5]
错集
// 差集
let arr = [1, 2, 3, 4, 3, 1];
let arr2 = [2, 3, 4, 5, 2];
let result = [...new Set(arr)].filter( item => !(new Set(arr2).has(item)));
console.log(result); //[1]
2.14Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和方法:
- size 返回 Map 的元素个数
// 定义一个map
let s = new Map();
// 新增一个元素
s.set('name',"孙悟空");
s.set('change',function(){
console.log('我们是西游记师徒四人');
});
console.log(s.size); // 2
- set 增加一个新元素,返回当前 Map
// 定义一个map
let s = new Map();
// 新增一个元素
s.set('name',"孙悟空");
s.set('change',function(){
console.log('我们是西游记师徒四人');
});
let key = {
adress:"四人地方"
}
s.set(key,['花果山','高老庄','流沙河']);
console.log(s); // {'name' => '孙悟空', 'change' => ƒ, {…} => Array(3)} key就是第一个 value就是第二个
- get 返回键名对象的键值
console.log(s.get('name')); //孙悟空
console.log(s.get('change')); //ƒ (){console.log('我们是西游记师徒四人');}
- has 检测 Map 中是否包含某个元素,返回 boolean 值
console.log(s.has('name')); //true
console.log(s.has('nnn')); //false
clear 清空集合,返回 undefined
//清空
s.clear();
console.log(s); //Map(0) {size: 0}
//for of 遍历
for(let v of s){
console.log(v); // ['name', '孙悟空'] ['change', ƒ]
}
2.15 class类
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
- class 声明类
// ES5
let Phone = function(pin,price){
this.pin = pin,
this.price = price
};
Phone.prototype.call = function(){
console.log("我的手机很nice");
};
// 实例化对象
let Huawei = new Phone('红米',2499);
Huawei.call(); //我的手机很nice
console.log(Huawei); //Phone {pin: '红米', price: 2499}
// class类
class Shou {
// 构造方法 名字不可以修改
constructor(pin,price){
this.pin = pin,
this.price = price
}
// 方法必须是这种写法,不能使用ES5对象完整形式
call(){
console.log('我的手机也不错');
}
}
let result = new Shou('OPPO',2899);
result.call(); //我的手机也不错
console.log(result); //Shou {pin: 'OPPO', price: 2899}
-
constructor 定义构造函数初始化
-
extends 继承父类(3.4一个案例)
-
super 调用父级构造方法
class Phone {
// 构造方法
constructor(pin,price){
this.pin = pin,
this.price = price
}
// 父类的成员属性
call(){
console.log('我是手机');
}
}
class SmartPhoen extends Phone {
// 构造方法
constructor(pin,price,color,size) {
super(pin,price);
this.color = color;
this.size = size;
}
photo(){
console.log("拍照");
}
game(){
console.log("打游戏");
}
}
let yhl = new SmartPhoen("红米",2999,"渐变色","1.23");
console.log(yhl); //SmartPhoen {pin: '红米', price: 2999, color: '渐变色', size: '1.23'}
yhl.call(); //我是手机
yhl.photo();//拍照
yhl.game(); //打游戏
- static 定义静态方法和属性
class Phone{
static name = "孙悟空";
static change = function (){
console.log('我的手机可以');
};
}
let yhl = new Phone();
console.log(yhl.name); //undefided
console.log(Phone.name); // 孙悟空
console.log(Phone.change); //ƒ (){console.log('我的手机可以');}
- 父类方法可以重写
调用的是紫类call方法,不是父类。在js语法中子类是不可以直接调用父类的
class Phone {
// 构造方法
constructor(pin,price){
this.pin = pin,
this.price = price
}
// 父类的成员属性
call(){
console.log('我是手机');
}
}
class SmartPhoen extends Phone {
// 构造方法
constructor(pin,price,color,size) {
super(pin,price);
this.color = color;
this.size = size;
}
photo(){
console.log("拍照");
}
game(){
console.log("打游戏");
}
call(){
console.log("我可以视频通话");
}
}
let yhl = new SmartPhoen("红米",2999,"渐变色","1.23");
console.log(yhl); //SmartPhoen {pin: '红米', price: 2999, color: '渐变色', size: '1.23'}
yhl.call(); //我可以视频通话
yhl.photo();//拍照
yhl.game(); //打游戏
2.16数值扩展
2.16.1 二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示 十六进制用0x表示。
let a = 0b0101; //二进制
let b = 0o0014 // 八进制
let c = 100; //十进制
let d = 0x0014; // 十六进制
console.log(a); //5
console.log(b); //12
console.log(c); //100
console.log(d); //20
2.16.2 Number.isFinite()与 Number.isNaN()
Number.isFinite() 用来检查一个数值是否为有限的
console.log(Number.isFinite(100)); //true
console.log(Number.isFinite('hello')); //false
Number.isNaN() 用来检查一个值是否为 NaN ,如果是一个数字就返回false 如果不是一个数字就返回true
console.log(isNaN(123)); //false
console.log(isNaN('hello')); //true
2.16.3Number.parseInt() 与 Number.parseFloat()
ES6 将全局方法 parseInt 和 parseFloat,字符串转整数。
let a = '5211314Iloveyou';
let b = '3.1415926yuanzhoulv';
console.log(Number.parseInt(a)); //5211314
console.log(Number.parseFloat(b)); // 3.1415926
2.16.4 Math.trunc
用于去除一个数的小数部分,返回整数部分。
console.log(Math.trunc(2.3)); //2
2.16.5 Number.isInteger
Number.isInteger() 用来判断一个数值是否为整数
console.log(Number.isInteger(1)); //true
console.log(Number.isInteger(1.3)); //false
2.17 对象的方法扩展
ES6 新增了一些 Object 对象的方法
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
console.log(Object.is(NaN,NaN)); //true
console.log(NaN === NaN); //false
- Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
传递两个参数 第一个参数是目标对象 第二个参数是源对象 如果有同样的属性名 会进行覆盖
onst obj = {
name:"孙悟空",
chenghao:"齐天大圣",
adress:"花果山"
}
const obj1 = {
name: "猪八戒",
adress:"高老庄",
gender:"男"
}
let result = Object.assign(obj,obj1);
console.log(result); //{name: '猪八戒', chenghao: '齐天大圣', adress: '高老庄', gender: '男'}
- setPrototypeOf、 setPrototypeOf 可以直接设置对象的原型
const team = {
name:"师徒四人"
};
const adress = {
diFang : ["花果山","高老庄","流沙河"]
};
Object.setPrototypeOf(team,adress);
console.log(team); //可以在原型上看见
2.18模块化
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
2.18.1 模块化的好处
模块化的优势有以下几点:
-
防止命名冲突
-
代码复用
-
高维护性
2.18.2 模块化规范产品
ES6 之前的模块化规范有:
- CommonJS => NodeJS、Browserify 2) AMD => requireJS 3) CMD => seaJS
2.18.3ES6模块化语法
模块功能主要由两个命令构成:export 和 import。
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
2.18.1 export 三种暴露方式
-
分别暴露
//m1.js // 分别暴露 export let name = '孙悟空'; export function fun(){ console.log('我可以三打白骨精'); }; //引入 在script标签里 需要有type="module" import * as m1 from './m1.js'; console.log(m1); //Module {Symbol(Symbol.toStringTag): 'Module'} 里面会有孙悟空 fun函数
-
统一暴露
//m2.js //统一暴露 let name = '猪八戒'; function fun(){ console.log("我能回高老庄"); } export {name,fun}; //引入 在script标签里 需要有type="module" import * as m1 from './m2.js'; console.log(m2); //Module {Symbol(Symbol.toStringTag): 'Module'} 里面会有猪八戒 fun函数
-
默认暴露
//m3.js //默认暴露 export default { name : '沙和尚', function:function(){ console.log('我在流沙河'); } } //引入 在script标签里 需要有type="module" import * as m1 from './m3.js'; console.log(m3); //Module {Symbol(Symbol.toStringTag): 'Module'} 里面有沙和尚 fun函数, 不过里面会多一层 default
2.18.2 ES6引入模块数据
-
通用的导入的方式
import * as m1 from './m1.js'; console.log(m1); //下图 import * as m1 from './m2.js'; console.log(m2); //下图中的孙悟空变成猪八戒 import * as m1 from './m3.js'; console.log(m3); //下图
-
-
解构赋值形式
// 解构赋值类型 import{name,fun} from './m1.js'; console.log(name); //孙悟空 console.log(fun); //ƒ fun(){console.log('我可以三打白骨精');} // 如果出现重名 可以通过起别名的方法解决 import{name as name1 ,fun as fun1} from './m2.js'; console.log(name1); //猪八戒 console.log(fun1); //ƒ fun(){console.log("我能回高老庄");} import {default as m3} from './m3.js' console.log(m3); //{name: '沙和尚', function: ƒ}
-
简便形式(只针对 默认暴露)
// 简单类型 只对默认暴露有用 import m3 from './m3.js' console.log(m3); //{name: '沙和尚', function: ƒ}
第三章 ECMASript 7 新 特 性
3.1 includes
方法用来检测数组中是否包含某个元素,返回布尔类型值 之前可以用indexOf(),没有返回-1
let arr = ['孙悟空','猪八戒','沙和尚','唐僧'];
console.log(arr.indexOf('白骨精')); //-1
console.log(arr.includes('孙悟空')); //true
console.log(arr.includes('小白龙')); //false
3.2 指数操作符
在 ES7 中引入指数运算符「**」,用来实现幂运算,功能与 Math.pow 结果相同
let a = 2 ** 5;
let b = Math.pow(2,5)
console.log(a); //32
console.log(b); //32
第四章 ECMAScript 8 新特性
4.1. async 和 await
async 和 await 两种语法结合可以让异步代码像同步代码一样
4.1.1. async 函数
- async 函数的返回值为 promise 对象,
- promise 对象的结果由 async 函数执行的返回值决定
async function fun(){
// 返回一个字符串
// 返回的结果不是一个Promise类型的对象 返回的结果就是成功的Promise对象
// return 'ok'
// 抛出错误就会返回一个失败的Promise
// throw new Error('失败啦');
// 返回的是一个promise对象
// 如果里面的是一个成功的promise async函数也会是一个成功的 否则就是失败的
return new Promise((resolve,reject)=>{
// resolve('获取到了数据');
reject('没有获取到数据');
})
};
let fn = fun();
// console.log(fn); //是一个成功的Promise ok就是他的返回值
// console.log(fn); //一个失败的Promise
console.log(fn); //是一个成功的promise 返回值是获取到了数据
console.log(fn); // 是一个失败的promise
4.1.2. await表达式
- await 必须写在 async 函数中
- await 右侧的表达式一般为 promise 对象
- await 返回的是 promise 成功的值
- await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
// 创建一个Promise对象
let a = new Promise((resolve, reject) => {
resolve('成功啦')
// reject('失败啦')
})
// await 必须在 async这个函数中 如果失败就用try catch
async function fun() {
try {
let result = await a;
console.log(result); //成功啦
} catch (error) {
console.log(error); //失败啦
}
}
fun();
4.1.3 async 和await使用读取文件
// 引入fs模块
const fs = require('fs')
// 使用promsie封装练习的古诗
function test(){
return new Promise((resolve,reject)=>{
fs.readFile('./练习.txt',(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
// 使用promsie封装练习2的古诗
function test2(){
return new Promise((resolve,reject)=>{
fs.readFile('./练习2.txt',(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
// 使用promsie封装练习3的古诗
function test3(){
return new Promise((resolve,reject)=>{
fs.readFile('./练习.txt',(err,data)=>{
if(err) reject(err);
resolve(data)
})
})
}
// 声明一个async函数
async function fun(){
let result = await test();
let result1 = await test2();
let result2 = await test3();
// 获取练习的古诗内容
console.log(result.toString());
// 获取练习2的古诗内容
console.log(result1.toString());
// 获取练习3的古诗内容
console.log(result2.toString());
};
fun(); //运行结果如下
4.2. Object.values 和 Object.entries
-
Object.values()方法返回一个给定对象的所有可枚举属性值的数组
-
Object.entries()方法返回一个给定对象自身可遍历属性 [key,value] 的数组
const yhl = { name:"孙悟空", age:18, sex:"男", adress:'花果山' } console.log(Object.keys(yhl)); //['name', 'age', 'sex', 'adress'] console.log(Object.values(yhl)); // ['孙悟空', 18, '男', '花果山'] console.log(Object.entries(yhl));//运行结果如下
4.3. Object.getOwnPropertyDescriptors
该方法返回指定对象所有自身属性的描述对象
console.log(Object.getOwnPropertyDescriptors(yhl));
//运行代码如下
第五章 ECMAScript 9 的特性
5.1Rest和spread
Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组, 在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
rest参数
function fun({name,age,...user}){
console.log(name); // 孙悟空
console.log(age); //18
// console.log(adress); //花果山
// console.log(chuoHao); //齐天大圣
console.log(user); // {adress: '花果山', chuoHao: '齐天大圣'} 会把剩下的属性,放到user对象中
};
fun({
name:"孙悟空",
age:18,
adress:"花果山",
chuoHao:"齐天大圣"
})
spread 扩展运算符
const obj = {
name:"孙悟空"
}
const obj1 = {
age:18
}
const obj2 = {
adress:"花果山"
}
const obj4 = {...obj,...obj1,...obj2}
console.log(obj4); //{name: '孙悟空', age: 18, adress: '花果山'}
第 6 章 ECMASript 10 新 特 性
6.1. Object.fromEntries
参数是一个二维数组,或者是一个map 可以将一个二维数组转化成一个对象
let arr = [['name','孙悟空'],['age',18]];
console.log(Object.fromEntries(arr)); //{name: '孙悟空', age: 18}
6.2.trimStart 和 trimEnd
// trim es5语法 清除两侧空白
let a = " 12 "
console.log(a+"没有清除空白"); //
console.log(a.trim() + "清除两侧空白"); //
// es10引进了 trimStart() 清除左侧空白 trimEnd()清除右侧空白
console.log(a.trimStart()+"清除左侧空白");
console.log(a.trimEnd() + "清除右侧空白");
运行结果
6.3.Array.prototype.flat与 flatMap
// flat 平 将多维数组转化为低位数组
let arr = [1,2,3,4,[5,6]];
console.log(arr.flat()); // [1, 2, 3, 4, 5, 6]
let arr2 = [1,2,3,4,[5,6,[7,8]]]
console.log(arr2.flat()); //[1, 2, 3, 4, 5, 6, Array(2)]
// 可以传递参数表示深度
console.log(arr2.flat(2));// [1, 2, 3, 4, 5, 6, 7, 8]
// mapFlat
let arr3 = [1,3,4];
// let a = arr3.map(item => item*10);
// console.log(a); //[10, 30, 40]
let a = arr3.flatMap(item => [item*10])
console.log(a); //[10, 30, 40]
6.4.Symbol.prototype.description
获取Symbol字符串描述
let s = Symbol('孙悟空');
console.log(s.description); //孙悟空
第 7 章 ECMASript 11 新 特 性
7.1. 类的私有属性
class Person {
// 公有属性
name;
// 私有属性 需要加一个#号
#age;
#wight;
// 构造方法
constructor(name, age, wight) {
this.name = name;
this.#age = age;
this.#wight = wight;
}
getName() {
console.log(girl.name); //白骨精
console.log(girl.#age); // 18
console.log(girl.#wight); //45kg
}
}
const girl = new Person("白骨精", 18, "45kg");
// console.log(girl.name);
// console.log(girl.#age); //会获取不到数据,外部不可以获取到
// console.log(girl.#wight);
// console.log(girl);//Person {name: '白骨精', #age: 18, #wight: '45kg'}
console.log(girl.getName());
7.2Promise.allSettled
接收一个promise数组,返回也是一个promise对象 返回的结果永远是成功的
const p1 = new Promise((resolve,reject) =>{
setTimeout((
resolve('获取成功 -1')
),1000)
});
const p2 = new Promise((resolve,reject) =>{
setTimeout((
// resolve('获取成功 -2')
reject('出错啦')
),1000)
});
// 返回的结果永远的成功的,就算有一个不会成功还是会返回成功的
// const result = Promise.allSettled([p1,p2]);
// console.log(result); //如下图
// 一旦有一个不成功 就会返回失败
const yhl = Promise.all([p1,p2])
console.log(yhl); //有一个有问题,就报错
7.3. 可选链操作符(?.)
使用场景:当我们对对象类型的参数的时候,层级比较多,有了它以后可以不进行层级判断
function fun(config) {
// 原始写法
// const db = config && config.name && config.name.name1;
// console.log(db); //123 如果不传就会报错
const db = config ?. name ?. name1;
console.log(db); //123 如果不传值 不会报错 会显示undefined
};
fun({
name: {
name1: "123",
host: "12334"
},
yhl: {
name1: "345",
host: "232323"
}
});
7.4. 动态 import 导入
点击一下按钮 控制台会打印出来hello
html代码
<button id="btn">点击一下</button>
<script src="./23测试.js" type="modele">
app.js代码
let btn = document.getElementById('btn');
btn.onclick = function(){
// 直接写路径
import('./24hello.js').then(module =>{
module.fun(); //hello
})
}
hello.js
//导出fun函数
export function fun(){
console.log('hello');
}
7.5. globalThis 对象
如果想对全局对象做一个操作,可以忽略环境直接使用 ,始终指向全局对象的。
7.6 BigInt
大整形
// 大整形 在整数后面直接加一个n就可以了
let a = 123n;
console.log(a,typeof a); //123n 'bigint'
// 函数
let s = 123;
console.log(BigInt(s)); //123n
console.log(BigInt(1.2)); //报错,不可以是小数
看着视频整理的,只是一个大概,好多细节知识还需要整理,如果有错误的地方或者有歧义的,欢迎大家指正。