ES6-ES11
一、ECMASript
1.1 什么是 ECMA
ECMA(European Computer Manufacturers Association)中文名称为欧洲计算机制 造商协会,这个组织的目标是评估、开发和认可电信和计算机标准。1994 年后该 组织改名为 Ecma 国际。
1.2.什么是 ECMAScript
ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化的脚本程序设计语言。
学习参考网站:https://es6.ruanyifeng.com/
1.3 ECMAScript 和 JavaScript 的关系
前者是后者的规格,后者是前者的一种实现。
JavaScript = ECMAScript + DOM +BOM
1.4 ES6兼容性
IE等低版本浏览器可能会不兼容ES6 需要使用Babel编译器进行编译为ES5在去执行
1.5 JavaScript数据类型
七种基本数据类型:
USONB
U:Undefined
S:String,Symbol
O:Object
N:Null,Number
B:Boolea
JavaScript的基本数据类型包括以下几种:
- 数字(Number):用于表示整数或浮点数,例如:1、3.14等。
- 字符串(String):用于表示文本数据,例如:“Hello World”、"CSDN"等。
- 布尔值(Boolean):用于表示真或假,只有两个取值:true和false。
- 空值(Null):表示一个空的或不存在的值。
- 未定义(Undefined):表示一个未定义的值,通常用于声明但未赋值的变量。
- 符号(Symbol):ES6引入的新类型,表示唯一的标识符。
除了以上基本数据类型,JavaScript还有一种引用数据类型:
- 对象(Object):用于存储多个键值对的集合,可以是自定义对象、数组、函数等
二、ES6
2.1 let 关键字
2.1.1 let定义变量
在ES5中变量只有全局和函数内部两种作用域,而在ES6中增加了块级作用域(通过let实现)
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
- 不允许重复声明 (避免重复声明造成变量污染)
- 块级作用域 (一对大括号内)
- 不存在变量提升 (不能先使用再声明)
- 不影响作用域链
应用场景:以后声明变量使用 let 就对了
2.1.2 let与var的区别
相同点:他们都可以用来定义一个变量。
不同点:
let定义的变量只在它所在的块中有效(一对 { } 就是一个块),var是全局变量;
let不存在变量位置的提升(必须先声明后使用),而var存在变量位置提升(即变量可以在声明之前使用,值为undefined);
let不允许在相同作用域内重复声明同一个变量,而var可以;
var可能会存在变量泄漏。
2.1.3 实践案例
通过for循环遍历元素绑定事件并触发
<script>
// 获取div元素对象
let items = document.getElementsByClassName("item");
// 遍历并绑定事件
for (let i = 0; i < items.length; i++) {
items[i].onclick = function () {
// 修改当前元素背景色 this指向当前元素
// this.style.background = 'pink';
items[i].style.background = "pink";
// 当i使用var定义时,当执行会回调函数时i为全局变量值为3
console.log(i); // 0 1 2
// 当i使用let定义时,i的作用域为块级作用域,当执行回调函数时i为初次循环的值
};
}
// console.log(i);
// var i=3
</script>使用var定义循环变量i时当事件触发时,i为全局变量值
js{ var i = 0; onclick = function(){ console.log(i); 3 } } { var i = 1; } { var i = 2; } var i = 3
使用let定义循环变量时,i为局部作用域,触发事件时,i的值为局部作用域的值
js{ let i = 0; onclick = function(){ console.log(i); 0 } } { let i = 1; } { let i = 2; }
2.2. const 关键字
const 关键字用来声明常量,const 声明有以下特点
常量:值不能修改
- 声明必须赋初始值
- 标识符一般为大写
- 不允许重复声明
- 值不允许修改 (指向的地址不允许改变,不能被整体替换)
- 块级作用域
注意: 对象属性修改和数组元素变化不会触发 const 错误
应用场景:声明对象类型使用 const,非对象类型声明选择 let
2.3 变量的解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称 为解构赋值。
2.3.1 数组解构
数组解构赋值就是下标的模式匹配
const f4 = ["张三", "李四", "王五"];
let [zs, ls, ww] = f4;
console.log(zs, ls, ww);2.3.2 对象解构
对象解构按照键名的模式匹配
const zhao = {
name: "赵四",
age: 30,
sex: "男",
xiaopin() {
// 对象方法简洁写法
console.log("我们可以演小品");
},
};
let { name, sex, age: nianling } = zhao;
// age: nianling 为age取别名
console.log(name, sex, nianling);
let {xiaopin} = zhao;
xiaopin();注意:频繁使用对象方法、数组元素,就可以使用解构赋值形式
2.3.3 复杂解构
//复杂解构
const wangfei = {
name: "王菲",
age: 18,
songs: ["红豆", "流年", "暧昧", "传奇"],
history: [{ name: "窦唯" }, { name: "李亚鹏" }, { name: "谢霆锋" }],
};
let {
songs: [one, two, three],
history: [first, second, third],
} = wangfei;
console.log(one, two, three, first, second, third);2.4.模板字符串
模板字符串(template string)是增强版的字符串,用反引号(`)标识,特点:
- 字符串中可以出现换行符,无需续行符
\ - 可以使用 ${xxx} 形式输出变量
// 1. 声明
let str = `我也是一个字符串`;
console.log(str, typeof str);
// 2. 内容中可以直接出现换行符
let str2 =
"<ul>\
<li>沈腾</li>\
<li>玛丽</li>\
</ul>";
let str3 = `<ul>
<li>沈腾</li>
<li>玛丽</li>
</ul>`;
// 3. 模板字符串中可以使用变量
let name = "王五";
let str4 = `他的名字是${name}`;
console.log(str4);注意:当遇到字符串与变量拼接的情况使用模板字符串
2.5 字符串方法
- String.fromCodePoint(unicode):获取指定unicode编码所对应的字符
console.log(String.fromCodePoint(0x20BB7)); //𠮷 ES6编译出为正确结果ES5中提供了 String.fromCharCode() 方法,但如果Unicode码位数较多时前两位会被舍弃,造成转译不准确
- str.codePointAt(index):获取str字符串指定位置字符所对应的unicode编码
let str = "abc嗨";
let code1 = str.codePointAt(0); //将str字符串下标为0的字符转换为所对应的Unicode编码- normalize():将字符的多种表示方法统一为同样的形式,即 Unicode 标准化化
console.log("\u01D1".normalize() === "\u004F\u030C".normalize());- includes(str):判断字符串中是否存在指定字符,返回布尔值
let str = "hi! welcome to font-end!";
console.log(str.includes("to")); // str 中是否有字符串 to
console.log(str.includes("to",10)); // 从下标10 开始找- startsWith(str):判断字符串是否以指定字符开头,返回布尔值
console.log(str.startsWith("hi")); // 判断str是否以hi开头
console.log(str.startsWith("welcome", 4)); //true 从指定下标开始匹配是否以welcome开头- endsWith(str):判断字符串是否以指定的字符结尾,返回布尔值
console.log(str.endsWith("!")); //判断字符串是否以!结尾
console.log(str.endsWith("font", str.length - 5)); //判断前指定个数的字符是否以font结尾- repeat(num):重复字符串num次
console.log("*".repeat(10)); // *重复10次
console.log("abc".repeat('3'));//数字字符串会自动转换为数字
console.log(str.repeat(2.8)); // 参数自动向下取整 参数>=0- padStart(num,str):字符串总长度为num,长度不足左边填充str字符
console.log('hello'.padStart(10, '*')); //*****hello 总长度为10,长度不够左侧补*- padEnd(num,str):字符串总长度为num,长度不足右边填充str字符
console.log('hello'.padEnd(10, '*'));; //hello***** 总长度为10,长度不够右侧补*2.6 简化对象写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
2.6.1 属性简写
键和值(仅当值为变量时)同名时,值可以省略
let name = "张三";
let change = function () {
console.log("change");
};
const learn = {
name,
// name:name
change,
// change:change
};2.6.2 方法简写
const obj = {
name: "test",
sing() {
console.log("唱");
},
jump: function () {
console.log("跳");
},
rap: () => {
console.log("rap");
},
};2.7 箭头函数
2.7.1 定义&调用
ES6 允许使用「箭头」(=>)定义函数。
// 声明一个函数
let fn1 = function () {};
let fn2 = (a, b) => {
return a + b;
};
// 调用函数
let result = fn2(1, 2);
console.log(result);2.7.2 箭头函数简写
如果形参只有一个,则小括号可以省略
jslet pow = a => { console.log(Math.pow(a, 2)); }; pow(2);函数体如果只有一条语句,则花括号可以省略,return语句也必须省略,且语句的执行结果就是函数的返回值
jslet add = (a, b) => a + b; console.log(add(1, 2));
2.7.3 箭头函数使用注意
箭头函数 this 是静态的 始终指向声明时所在作用域下 this 的值,即指向外层作用域的this。且使用call aply bind 都不能改变箭头函数中的this
jsfunction getName() { console.log(this.name); } let getName2 = () => { console.log(this.name); }; // 设置 window对象的 name 属性 window.name = "张三"; const obj = { name: "李四", }; // 直接调用 getName(); // 张三 getName2(); // 张三 // call方法调用,改变this的指向 getName.call(obj); // 李四 getName2.call(obj); // 张三箭头函数不能作为构造函数实例化
jslet Person = (name,age)=>{ this.name = name; // this指向window this.age = age; } let me = new Person("xinxin",20); console.log(me); // Person is not a constructor不能使用 arguments
jslet fn = () => { console.log(arguments); }; fn(1, 2, 3); // arguments is not defined需要动态 this 的时候,不应使用箭头函数,如在用this获取触发事件的元素对象时
jsconst button = document.getElementById("button"); button.onclick = () => { console.log(this); // window this.style.background = "red"; // 报错 }; button.onclick = function () { console.log(this); // buttom元素 this.style.background = "red"; };箭头函数定义的对象方法 this 指向 window
let obj = {
name: "张三",
getname() {
console.log(this.name); // this 指向obj
},
getName: () => {
console.log(this.name); // this 指向window
},
};2.7.4 使用场景
箭头函数适合与 this 无关的回调,定时器、数组方法的回调
箭头函数不适合与 this 有关的回调,事件回调(使用事件源)、对象的方法(在对象方法中使用this)
注意:箭头函数不会更改 this 指向,且指向外层作用域的this,用来指定回调函数会非常合适
- 案例1定时器内操作外层元素对象:
<body>
<div id="ad"></div>
<script>
//需求-1 点击 div 2s 后颜色变成『粉色』
// 获取元素
let ad = document.getElementById("ad");
// 绑定事件
ad.addEventListener("click", function () {
// ES5 做法
// 保存 this
let _this = this;
// 定时器
window.setTimeout(function () {
console.log(this); // this 指向 window setTimeout属于window下的方法
this.style.background = "pink"; // 报错
// 解决办法 使用外部this
_this.style.background = "pink";
}, 2000);
// 使用箭头函数 this指向为静态的始终指向声明时所在的作用域下的this,即指向外层作用域的this
setTimeout(() => {
console.log(this);
this.style.background = "pink";
}, 2000);
});
</script>
</body>- filter() 使用箭头函数简化代码
filter()方法用于创建一个新的数组,包含通过回调函数返回为 true 的所有元素。filter()方法会遍历一个数组,对每个元素执行回调函数,然后返回所有使得该函数返回值为true的元素组成的新数组
// 需求-2 从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25];
const newresult = arr.filter(function (item) {
if (item % 2 === 0) {
return true;
} else {
return false;
}
});
// 使用箭头函数 简化代码
const newresult = arr.filter((item) => item % 2 == 0);
console.log(newresult); // [6, 10, 100]2.8 函数参数默认值
ES6 允许给函数参数赋值初始化
可以方便明确的知道哪些参数有默认值,是可以省略的
// 1.形参初始值 具有默认值的参数,位置一般靠后
add = (a, b, c = 0) => a + b + c;
let result = add(1, 2);
console.log(result);
// 2.默认参数与解构赋值结合
function connect({ host, username, password, port='3306' }) {
console.log(host, username, password, port);
}
connect({
host: "localhost",
username: "root",
password: "root",
});注意:具有默认值的参数,位置一般靠后
2.9 rest 参数
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
// ES5 获取实参的方式
function date() {
console.log(arguments); // object
}
date("a", "b", "c");
// ES6 rest 参数
function data2(...args) {
console.log(args); // ['a', 'b', 'c']
}
data2("a", "b", "c");
// rest 参数必须位于参数的最后面
function data3(a, b, ...rest) {
console.log(a, b); // 1,2
console.log(rest);
}
data3(1, 2, 3, 4); // [3, 4]作用与 arguments 类似
rest 参数必须是最后一个形参
注意:rest 参数非常适合不定个数参数函数的场景
2.10 扩展运算符
2.10.1 认识扩展运算符
... 扩展运算符(spread)能将数组转换为逗号分隔的参数序列
const arr = ['张三','李四','王五'];
console.log(...arr);// '张三','李四','王五'2.10.2 扩展运算符的应用
数组的合并
js// 1.数组的合并 const arr1 = [1, 2, 3]; const arr2 = [4, 5, 6]; // ES5 const newAarr1 = arr1.concat(arr2); console.log(newAarr1); // 扩展运算符 const newArr2 = [...arr1, ...arr2]; const newArr3 = Array.of(...arr1, ...arr2); // Array.of() 会把任意参数转成数组 console.log(newArr2, newArr3); // Arry.from() 将可迭代对象转换为数组数组的克隆
jsconst sanzhihua = ["E", "G", "M"]; const sanzhicao = [...sanzhihua]; // 浅拷贝 console.log(sanzhicao);将为数组转换为真正的数组
jsconst divs = document.querySelectorAll("div"); console.log(divs); // [ovject] NodeList(3) [div, div, div] 伪数组不能直接遍历 const divArr = [...divs]; console.log(divArr);
2.11 Symbol
2.11.1 Symbol 基本使用
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用 for…in 循环遍历 ,但是可以使用 Reflect.ownKeys 来获取对象的所有键名
// 创建 Symbol
let s = Symbol();
console.log(s, typeof s); // Symbol() 'symbol'
let s2 = Symbol("一不小鑫"); // 传递参数描述字符串
let s3 = Symbol("一不小鑫");
console.log(s2 === s3); // false
// Symbol.for() 创建
let s4 = Symbol.for("一不小鑫");
let s5 = Symbol.for("一不小鑫");
console.log(s4, typeof s4); //Symbol(一不小鑫) 'symbol'
console.log(s4 == s5); // true注: 遇到唯一性的场景时要想到 Symbol
2.11.2 Symbol创建对象属性
Symbol为对象添加属性和方法表示独一无二的
// 向对象中添加 up down 不破坏原有的属性和方法
let game = {
name: "游戏",
up() {},
down() {},
};
// 声明一个对象
let method = {
up: Symbol("新增的up方法"),
down: Symbol("新增的down方法"),
};
game[method.up] = function () {
console.log("我是up");
};
game[method.down] = function () {
console.log("我是down");
};
console.log(game);
game[method.up]();
let youxi = {
name: "王者荣耀",
// 添加独一无二的方法
[Symbol("libai")]: function () {
console.log("我是李白");
},
[Symbol("yase")]: function () {
console.log("我是亚瑟");
},
};
console.log(youxi);2.11.3 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 环境排除。 |
// 1. Symbol.hasInstance
class Person {
// 当其他对象使用instanceof时会自动调用
static [Symbol.hasInstance](param) {
console.log(param);
console.log("我被用来用于检测类型");
}
}
let o = {};
console.log(o instanceof Person);
// 2.Symbol.isConcatSpreadable
const arr = [1, 2, 3];
const arr2 = [4, 5, 6];
// 设置arr2使用concat方法合并数组时是否展开
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2)); //[1, 2, 3, Array(3)]2.12 迭代器
2.12.1 迭代器遍历
遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口(对象里的属性),就可以完成遍历操作。
- ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 使用
- 原生具备 iterator 接口的数据(可用 for of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
- 工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done(是否结束) 属性的对象
// 声明一个数组
const arr = ["A", "B", "C", "D"];
// 使用 for....of 遍历数组
for (let item of arr) {
// 保存数组中的键值
console.log(item);
}
// 使用 for...in 遍历数组
for (let i in arr) {
// 保存数组的键名(下标)
console.log(i);
}
console.log(arr); // 存在Symbol(Symbol.iterator)
let iterator = arr[Symbol.iterator]();
console.log(iterator); // 存在 next()方法
console.log(iterator.next()); //{value: 'A', done: false}
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next()); //{value: undefined, done: true}2.12.2 迭代器自定义遍历数据
// 声明一个对象
const banji = {
name: "一班",
stus: ["xiaomming", "xiaoning", "xiaotian"],
// 创建interator接口
[Symbol.iterator]() {
// 索引变量
let index = 0;
let _this = this;
return {
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...of遍历这个对象每次返回数组中的成员
for (let v of banji) {
// 使用for...of遍历必须要有iterator接口
console.log(v);
}注: 需要自定义遍历数据的时候,要想到迭代器。
2.13 生成器
2.13.1 生成器函数声明与调用
生成器函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同
// 生成器函数其实就是一个特殊的函数
// 异步编程 纯回调函数
function* gen() {
console.log("111");
// 函数代码的分隔符
yield "一只没有耳朵";
console.log("222");
yield "一只没有尾巴";
console.log("333");
yield "真奇怪";
console.log("444");
}
let iterator = gen();
console.log(iterator);
iterator.next(); // 111
iterator.next(); // 222
iterator.next(); // 333
iterator.next(); // 444
// 遍历
for (let v of gen()) {
console.log(v);
}代码说明:
- *的位置没有限制 在function和函数名之间
- 生成器函数返回的结果是迭代器对象,调用迭代器对象的 next 方法可以得到 yield 语句后的值
- yield 相当于函数的暂停标记,也可以认为是函数的分隔符,每调用一次 next 方法,执行一段代码
- next 方法可以传递实参,作为 yield 语句的返回值
2.13.2 生成器函数的参数传递
function* gen(arg) {
console.log(arg); // AAA
let one = yield 111;
console.log(one); //BBB
let two = yield 222;
console.log(two); // CCC
let three = yield 333;
console.log(three); // DDD
}
// 执行获取迭代器对象
let iteratir = gen("AAA");
// next方法可以传入实参
console.log(iteratir.next()); //{value: 111, done: false}
// 在第二次调用next方法传递的实参,会作为第一个yield表达式的值
console.log(iteratir.next("BBB"));
console.log(iteratir.next("CCC"));
console.log(iteratir.next("DDD")); //{value: undefined, done: true}2.13.3 回调地狱
多个回调函数嵌套,会产生回调地狱
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);2.13.4 生成器函数实例
需求:1s 后控制台输出 111 2s 后控制台输出 222 3s后输出 333
// 单个任务封装
function one() {
setTimeout(() => {
console.log(111);
iterator.next();
}, 1000);
}
function two() {
setTimeout(() => {
console.log(222);
iterator.next();
}, 2000);
}
function three() {
setTimeout(() => {
console.log(333);
iterator.next();
}, 3000);
}
// 将三个函数的调用放在了yield中
function* gen() {
yield one();
yield two();
yield three();
}
// 调用生成器函数生成一个迭代器对象
let iterator = gen();
iterator.next(); // 调用迭代器对象的next方法3.13.5 生成器函数实例2
模拟获取 用户数据 订单数据 商品数据
在next函数中传递参数
function getUsers() {
setTimeout(() => {
let data = "用户数据";
// 调用next 方法,并且将数据传入
iterator.next(data);
// 第二次调用next方法传入实参将作为第一个yield的返回值
}, 1000);
}
function getOrders() {
setTimeout(() => {
let data = "订单数据";
// 第三次调用next方法传入实参作为第二个yield的返回值
iterator.next(data);
}, 1000);
}
function getGoods() {
setTimeout(() => {
let data = "商品数据";
iterator.next(data);
}, 1000);
}
// 声明一个生成器函数
function* gen() {
// 接收第一个yield的返回值(为第二个next方法传入的参数)
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(); //调用迭代器对象的next方法2.14 Promise
2.14.1 Promise介绍
Promise 是 ES6 引入的异步编程的新解决方案,用来避免回调地狱。语法上 Promise 是一个构造函数, 用来封装异步操作并可以获取其成功(resolved)或失败(rejected)的结果。
- Promise 构造函数: Promise (excutor) {}
- Promise.prototype.then 方法
- Promise.prototype.catch 方法
Promise对象的两个特点
- 对象的状态不受外界影响
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
2.14.2 Promise基本语法
// 实例化 Promise 对象,回调函数需要传递两个参数,resolve(变为成功状态) 和 reject(变为失败状态)
const p = new Promise(function (resolve, reject) {
setTimeout(() => {
// 读取数据成功
/* let data = "数据库中的数据";
resolve(data);
// resolve 改变状态为成功,调用then中的第一个回调函数,并传递成功信息 */
// 读取数据失败
let err = "数据读取失败";
reject(err); //改变状态为失败,调用then中的第二个回调函数,并传递失败信息
}, 1000);
});
// 调用promise对象的then方法,有两个回调函数,第一个为成功回调,第二个为失败回调
p.then(
function (value) { // 成功回调
console.log(value);
},
function (reason) { // 失败回调
console.log(reason);
}
);2.14.3 Promise封装读取文件
需求:读取resources目录下的为学.md文件
// 1.引入 fs 模块
const fs = require("fs");
// 2.使用Promise封装
const p = new Promise((resolve, rejecct) => {
fs.readFile("./resources/为学.md", (err, data) => {
//错误优先回调
// 如果读取失败
if (err) rejecct(err);
// 如果读取成功
resolve(data);
});
});
// 使用then方法处理成功失败的回调
p.then(
(value) => {
console.log(value.toString());
},
(reason) => {
console.log("读取失败");
}
);2.14.4 Promise封装AJAX请求
- 封装原生ajax请求
const p = new Promise((resolve, reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest();
// 2.初始化
xhr.open("GET", "https://api.apiopen.top/getJoke");
// 3.发送
xhr.send();
// 4.绑定事件,处理结果
xhr.onreadystatechange = function () {
// 判断状态是否为4,表示请求已经完成
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.log(reason);
}
);封装JQuery AJAX请求
const getInfo = () => {
return new Promise((resolve, reject) => { //返回一个Promise对象
// Ajax请求
$.ajax({
type: 'get',
url: '/api/info',
success: function(data) {//请求成功
resolve(data);//调用resolve函数,将promise对象状态变为成功,并将异步操作的结果作为参数传递出去;
},
error: function(err) {//请求失败
reject(err);//调用reject函数,将promise状态转变为失败,并将错误信息作为参数传递出去
}
});
});
};
getInfo().then(response => { //返回请求成功的数据,response为调用resolve函数传递出的数据
console.log(response);
}).catch(err =>{ //返回错误信息,err为调用reject函数传递出的数据
console.log(response);
});2.14.5 Promise.prototype.then()方法返回结果状态
调用 then 方法,then方法的返回结果是 Promise对象,对象的状态由回调函数的执行结果决定
- 如果回调函数返回值是 非Promise 类型的属性,状态为成功,返回值为对象的成功的值
- 如果回调函数返回值是 promise 类型的属性,状态由返回的Promise的状态决定
- 抛出错误 Promise 对象的状态为失败
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("用户数据");
// reject("出错了!");
}, 1000);
});
const result = p.then(
(value) => {
console.log(value); // 用户数据
// 1.非 Promise 类型的属性
return "123";
// 2.是 promise 对象
return new Promise((resolve, reject) => {
resolve("ok");
// reject("出错了");
});
// 3.抛出错误
throw new Error("出错了");
// throw '出错了';
},
(reason) => {
console.warn(reason);
}
);then 方法返回的是一个Promise对象,因此可以链式调用,在then方法中执行异步任务,再通过then方法处理
p.then(
(value) => {},
(reason) => {}
).then(
(value) => {},
(reason) => {}
);2.14.6 Promise读取多个文件
需求:逐个读取resourses文件夹下的三个文件,再将其内容拼接输出
- 回调函数嵌套回调函数实现
// 引入fs模块
const fs = require("fs");
fs.readFile("./resources/为学.md", (err, data1) => {
if (err) throw err;
fs.readFile("./resources/插秧诗.md", (err, data2) => {
if (err) throw err;
fs.readFile("./resources/观书有感.md", (err, data3) => {
let result = data1 + "\r\n" + data2 + "\r\n" + data3; // 字符串拼接会自动将buffer转换为字符串
console.log(result);
});
});
});产生回调地狱,会出现命名冲突,代码缩进问题
- 使用 Promise 解决地狱回调问题
// 引入fs模块
const fs = require("fs");
const p = new Promise((resolve, reject) => {
// 读取第一个文件内容
fs.readFile("./resources/为学.md", (err, data) => {
if (err) reject(err);
resolve(data);
});
});
p.then((value) => {
// console.log(value.toString()); // 上次Promise返回成功状态的数据
// 返回一个Promise对象进行链式调用
return new Promise((resolve, reject) => {
// 读取第二个文件内容
fs.readFile("./resources/插秧诗.md", (err, data) => {
if (err) reject(err);
// 将读取的内容作为promise的返回值传递给then方法
resolve(value + "\r\n" + data);
});
});
})
.then((value) => {
// console.log(value);
// 读取第三个文件内容
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
if (err) reject(err);
resolve(value + "\r\n" + data);
});
});
})
.then((value) => {
console.log(value);
});解决层层回调嵌套,和命名冲突问题
2.14.6 catch 方法
用于指定Promise对象失败的回调
// 指定成功失败的回调
// 方法一
p.then(
(value) => {
console.log(value);
},
(reason) => {
console.log(reason);
}
);
// 方法二 then方法不指定第二个参数,使用catch代替then中的第二个错误回调
p.then((value) => {
console.log(value);
}).catch((reason) => {
console.warn(reason);
});2.15 Set
2.15.1 集合介绍与API
ES6 提供了新的数据结构 Set(集合)。它类似于数组,但成员的值都是 唯一 的,集合实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进 行遍历,集合的属性和方法:
- size 返回集合的元素个数
- add 增加一个新元素,返回当前集合
- delete 删除元素,返回 boolean 值
- has 检测集合中是否包含某个元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 1.创建
// 声明一个 set
let s = new Set();
// 传入初始参数(可迭代数据)
let s2 = new Set([1, 2, 2, 3, 4, 4]); // 会自动进行去重
console.log(s2, typeof s2);
// 2.属性
// 元素的个数
console.log(s2.size); // 4 去重后的个数
// 添加新的元素
s2.add(5);
// 删除元素
s2.delete(2);
// 检测元素是否存在
console.log(s2.has(3));
// 清空集合
// s2.clear();
console.log(s2);
// 使用 for...of进行遍历
for (v of s2) {
console.log(v);
}2.15.2 集合实践
数组去重
jslet arr = [1, 2, 3, 4, 5, 4, 2, 3, 1]; let s1 = new Set(arr); console.log([...s1]); // console.log(Array.of(...s1));交集
jslet arr = [1, 2, 3, 4, 5, 4, 2, 3, 1]; let arr2 = [4, 5, 6, 5, 4, 6]; // filter回调函数形参为被遍历数组的元素,返回一个新数组,新数组中的元素是原始数组中符合条件(返回结果为true)的元素 let result = [...new Set(arr)].filter((item) => { // item 是数组中的元素 let s2 = new Set(arr2); // 集合 return s2.has(item); // 判断集合中是否有数组中的元素 }); // 代码简化 let result = [...new Set(arr)].filter((item) => new Set(arr).has(item)); console.log(result);并集
jslet union = [...new Set([...arr, ...arr2])]; console.log(union);差集
jslet difference = [...new Set(arr)].filter((item) => !new Set(arr2).has(item)); // 返回结果为true 将item添加到新数组中 console.log(difference);
2.16 Map
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。但是“键” 的范围不限于字符串,各种类型的值(包括对象)都可以当作键。Map 也实现了 iterator 接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。Map 的属性和
方法:
- size 返回 Map 的元素个数
- set 增加一个新元素,返回当前 Map
- delete删除指定键名的元素
- get 返回键名对象的键值
- has 检测 Map 中是否包含某个元素,返回 boolean 值
- clear 清空集合,返回 undefined
// 声明一个Map对象
let m = new Map();
// 添加元素
m.set("name", "xinxin");
m.set("change", function () {
console.log("改变");
});
// 对象作为Map的键
let key = {
school: "ATGUIGU",
};
m.set(key, ["北京,上海,深圳"]);
// 删除元素
m.delete("name");
// 获取元素
console.log(m.get("change"));
console.log(m.get(key));
// 检测
console.log(m.has("name"));
// 清空
// m.clear();
// 遍历
for (let v of m.values()) {
console.log(v);
}
console.log(m, m.size);对象的key只能为字符串,Map的key可以为任意类型
2.17 class 类
2.17.1 calss介绍
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过 class 关键字,可以定义类。基本上,ES6 的 class 可以看作只是 一个语法糖,它的绝大部分功能,ES5 都可以做到,新的 class 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。
知识点:
- class 声明类
- constructor 定义构造函数初始化
- extends 继承父类
- super 调用父级构造方法
- static 定义静态方法和属性
- 父类方法可以重写
2.17.2 类声明
ES5 构造函数
function Phone(brand, price) {
this.brand = brand;
this.price = price;
}
// 添加方法
Phone.prototype.call = function () {
console.log("我可以打电话");
};
// 实例化对象
let Huawei = new Phone("华为", 4999);
console.log(Huawei);
Huawei.call();ES6 类声明
class Phone {
// 构造方法 名字固定,当使用new 类名时自动执行
constructor(brand, price) {
this.brand = brand;
this.price = price;
}
// 注意:方法必须使用该语法,不能使用ES5对象完整语法
call() {
// 方法挂载于原型上(提高复用避免每次创建时重新生成减少内存消耗)
console.log("可以打电话");
}
}
let xiaomi = new Phone("小米", 1999);
console.log(xiaomi);
xiaomi.call();2.17.3 静态成员
特征:
- 使用 static 声明属性和方法
- 不属于实例化对象,只能通过类进行调用
- 可以被子类继承
class Phone {
// 静态属性 属于类不属于实例化对象
static name = "手机"; // 静态属性
static change() { // 静态方法
console.log("科技改变世界");
}
}
let nokia = new Phone(); // 实例化对象
console.log(nokia.name); // undefined
// 可以通过类名进行调用
console.log(Phone.name); // 手机2.17.4 私有成员
特征:
- 使用 #声明属性和方法
- 只能在类的内部使用
- 不属于实例化对象
- 不能被继承
class Phone {
// 私有属性
#cpu = "apple";
active() {
// 调用私有方法
this.#call();
}
// 私有方法
#call() {
// 使用私有属性
console.log(`${this.#cpu}可以视频电话`);
}
}
let iphone = new Phone();
iphone.active(); //apple可以视频电话
// iphone.#active(); 报错
// console.log(iphone.#cpu); 报错
// Phone.#cpu 报错 只能再类内部使用2.17.5 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); // 改变Phone中this的指向,指向SmartPhone的实例化对象
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 xiaomi = new SmartPhone("小米", 1999, "白色", 5.5);
console.log(xiaomi);
xiaomi.call();2.17.6 class的类继承
使用 extends 继承
使用 super 调用父类的构造方法
除了私有成员不能被继承,其它的属性和方法的可以被继承
实现效果与构造函数相同
// 父类
class Phone {
// 构造方法
constructor(brand, price) {
this.brand = brand;
this.prcie = price;
}
// 父类的成员方法
call() {
console.log("我可以打电话");
}
}
// 子类 继承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("玩游戏");
}
}
// 实例化子类
const xiaomi = new SmartPhone("小米", 1999, "白色", 5.5);
console.log(xiaomi);
xiaomi.call();
xiaomi.photo();2.17.7 父类方法的重写
在子类声明一个与父类同名的方法
子类实例化对象调用时执行子类重写的方法
// 父类
class Phone {
call(brand) {
console.log(`${brand}手机能打电话`);
}
}
// 子类
class SmartPhone extends Phone {
call() {
console.log("能打视频电话");
}
}
// 实例化子类
const xiaomi = new SmartPhone();
xiaomi.call(); // 能打视频电话 调用的是子类重写的方法不能使用super方法调用父类的成员方法
2.17.8 getter和setter
当对某一属性进行获取时调用getter方法
当对某一属性进行设置时调用setter方法
class Phone {
// 对动态属性进行封装
get price() {
console.log("价格属性被获取");
return "100";
}
set price(val) {
console.log("价格属性被设置");
}
}
// 实例化对象
let s = new Phone();
console.log(s.price);
s.price = "free";2.18 数值扩展
2.18.1 二进制和八进制
ES6 提供了二进制和八进制数值的新的写法,分别用前缀 0b 和 0o 表示。
let b = 0b1010; // 二进制
console.log(b); // 10
let o = 0o777; // 八进制
console.log(o); // 511
let d = 100; // 十进制
console.log(d); // 100
let x = 0xff; // 十六进制
console.log(x); // 2552.18.2 Number.isFinite() 与 Number.isNaN()
Number.isFinite() 用来检查一个数值是否为有限的
console.log(Number.isFinite(100)); // true
console.log(Number.isFinite(Infinity)); // falseNumber.isNaN() 用来检查一个值是否为 NaN(not a number:非数字)
console.log(Number.isNaN(123)); //fasle
console.log(isNaN(123)); // false
console.log(isNaN("a")); // true2.18.3 Number.parseInt() 与 Number.parseFloat()
将字符串转换为Number类型
ES6 将全局方法 parseInt 和 parseFloat,移植到 Number 对象上面,使用不变。
console.log(Number.parseFloat("100.133ABC")); // 100.133
console.log(Number.parseInt("12.94hello")); // 12
console.log(parseInt("12")); // 12
console.log(parseFloat("12.44")); // 12.442.18.4 Number.isInteger
Number.isInteger() 用来判断一个数值是否为整数
console.log(Number.isInteger(12)); // true
console.log(Number.isInteger("12")); //false2.18.5 Number.EPSILON
Number.EPSILON是 Javascript 表示的最小精度
EPSILON 属性的值接近于 2.2 的 -16次方,这是最小的精度
当两个数的差值小于Number.EPSILON时,认为这两个数相等
console.log(0.1 + 0.2 === 0.3); // false
function equal(a, b) {
if (Math.abs(a - b) < Number.EPSILON) { // 当两个数的差的绝对值小于NUmber.EPSILON时认为两个数相等
return true;
} else {
return false;
}
}
console.log(equal(0.1 + 0.2, 0.3)); // true2.18.6 Math.trunc
用于去除一个数的小数部分,返回整数部分。
console.log(Math.trunc(12.999)); // 122.18.7 Math.sign
判断一个数到底是整数 负数 还是零
console.log(Math.sign(100)); // 1
console.log(Math.sign(0)); // 0
console.log(Math.sign(-10)); // -12.19 对象扩展
ES6 新增了一些 Object 对象的方法
- Object.is 比较两个值是否严格相等,与『===』行为基本一致(+0 与 NaN)
console.log(Object.is(1, 1.0)); // true
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false
console.log(Object.is(1, 1)); // true
console.log(Object.is([1], [1])); // false- Object.assign 对象的合并,将源对象的所有可枚举属性,复制到目标对象
const config1 = {
host: "localhost",
port: 3306,
name: "root",
pass: "root",
one: 1,
};
const config2 = {
host: "127.0.0.1",
port: 80,
name: "admin",
pass: "admin",
tow: 2,
};
// Object.assign(被覆盖,覆盖);
console.log(Object.assign(config1, config2)); //两者进行合并 config2会覆盖config1内的同名属性用于配置文件合并
- __ proto__、setPrototypeOf、 getPrototypeOf 可以直接设置对象的原型
const school = {
name: "xx",
};
const cities = {
xiaoqu: ["北京", "上海", "深圳"],
};
// 为school设置原型 将cities内容作为school的原型
Object.setPrototypeOf(school, cities);
// 获取原型
console.log(Object.getPrototypeOf(school));
console.log(school);一般不这样使用
2.20 模块化
模块化是指将一个大的程序文件,拆分成许多小的文件(模块),然后将小文件组合起来。
2.20.1 模块化的好处
模块化的优势有以下几点:
- 防止命名冲突
- 代码复用
- 高维护性
2.20.2 模块化规范产品
- ES6 之前的模块化规范有:
- CommonJS => NodeJS、Browserify
- AMD => requireJS
- CMD => seaJS
2.20.3 ES6 模块化语法
模块功能主要由两个命令构成:export 和 import。
- export 命令用于规定模块的对外接口
- import 命令用于输入其他模块提供的功能
2.20.4 export暴露
分别暴露
jsexport let school = 'xx'; export function teach(){ console.log("好好学习"); }统一暴露
jslet school = "xx"; function play() { console.log("让我们愉快的玩耍吧"); } export { school, play };默认暴露
将所有内容放在 default 对象下 对default对象进行暴露
jsexport default { name:"张三", age:'13', say(){ console.log(`我是${this.name}`); } }
注意:一个js文件只能有一个默认暴露
2.20.5 import 导入
通用的导入方式
js// 引入 分别/统一暴露 模块内容 import * as m1 from "./src/js/m1.js"; // 将m1.js下暴露的内容全部都存在 m1 对象中 console.log(m1); console.log(m1.school); m1.teach(); // 引入 默认暴露 模块内容 import * as m3 from "./src/js/m3.js"; console.log(m3); m3.default.say(); // 使用时需要加上default console.log(m3.default.name);解构赋值形式
js// 导入 分别/统一暴露 模块内容 import { school, teach as th } from "./src/js/m1.js"; // as 取别名 console.log(school); th(); // 导入 默认暴露 模块内容 import { default as m3 } from "./src/js/m3.js"; // 固定格式 默认暴露将要暴露的数据存放在default中,必须将default起别名 console.log(m3); m3.say();简便形式(只能用于默认暴露)
jsimport m3 from "./src/js/m3.js"; console.log(m3); m3.say();
注意:在使用script标签引入模块时要在加上 type="module"
<script type="module"></script>2.20.6 浏览器使用ES6 模块化方式
将引入模块的语句放在入口文件 app.js 中
然后再将 app.js 引入html页面中
app.js
import * as m1 from "./m1.js";
import * as m2 from "./m2.js";
import m3 from "./m3.js";
console.log(m1);
console.log(m2);
console.log(m3);index.html
<script src="" data-missing="app.js" type="module"></script>将一个大文件分割成多个小文件,将代码分散简化
2.20.7 babel对ES6模块化代码转换
在项目中一般不直接使用script标签引入入口文件,而是使用 babel 将 js 文件编译打包后将引入
原因:
- 不是所有的浏览器都识别 ES6 模块化语法
- ES6 模块化不能直接对npm安装的模块直接导入
babel官网:将ES6 转换为 ES5
步骤
安装工具 babel-cli babel-preset-env browserify(打包工具)
npm init
npm i babel-cli babel-preset-env browserify -D
编译输出文件
npx babel src/js -d dist/js --presets=babel-preset-env
打包
npx browserify dist/js/app.js -o dist/bundle.js
引入打包好的入口 bundle.js 文件
html<script src="" data-missing="bundle.js"></script>
2.20.8 ES6模块化引入npm包
需求:使用 jquery 修改html背景色
安装jquery
npm i jquery
导入jquery
jsimport $ from 'jquery'; // 等同于使用 commonJS: const $ = require('jquery)使用jquery修改背景色
js$('body').css('background','pink');重新编译打包
三、ES7 新特性
3.1 Array.prototype.includes
Includes 方法用来检测数组中是否包含某个元素,返回布尔类型值,功能与indexOf相同
// includes 判断指定元素是否在数组中
const names = ["张三", "李四", "王五"];
console.log(names.includes("李四")); // true
console.log(names.includes("赵六")); // false
// ES5 中使用 indexOf 返回值为下标,不存在返回-1
console.log(names.indexOf("李四")); // 13.2 指数操作符
在 ES7 中引入指数运算符「**」,用来实现幂运算,功能与 Math.pow 结果相同
// ** 幂运算
console.log(2 ** 10); // 1024
// ES5 中使用Math.pow()
console.log(Math.pow(2, 10)); // 1024四、ES8 新特性
4.1 async 和 await
async 和 await 两种语法结合可以让异步代码像同步代码一样
用于处理异步编程
4.1.1 async 函数
- 在函数名前加 async 该函数就变成了一个异步函数,不会阻塞该函数后面代码的执行
- async 函数的返回值为 promise 对象,
- promise 对象的结果由 async 函数执行的返回值决定
async function fn() {
// return返回的结果不是一个promise对象,则函数返回的结果为一个成功的promise对象
// return 123;
// 抛出错误,返回的结果是一个失败的promise对象
// throw new Error('错误');
// 返回结果是一个Promise对象
return new Promise((resolve, reject) => {
resolve("成功"); // 返回Promise的状态就是fn函数的状态
// reject("失败");
});
}
const result = fn();
console.log(result);
// 调用then方法
result.then(
(value) => {
console.log(value); // 成功
},
(reason) => {
console.warn(reason);
}
);4.1.2 await 表达式
- await 必须写在 async 函数中 await语句会阻塞async函数await语句后面的代码执行,直到await右侧的Promise对象状态改变
- await 右侧的表达式一般为 promise 对象
- await 接收的是 promise 成功的值
- await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理
// 创建Promise对象
const p = new Promise((resolve, reject) => {
// resolve("用户数据");
reject("失败了");
});
// await 要放在 async 函数中
async function main() {
// Promise 失败使用 try...catch进行错误捕获
try {
// await 返回Promise成功的值
let result = await p;
console.log(result);
} catch (e) {
console.log(e);
}
}
// 调用函数main
main();4.1.3 async与await结合读取文件
需求:逐个读取resources文件夹下的三个文件,并将结果进行拼接
// 引入 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 readGuanShuYouGan() {
return new Promise((resolve, reject) => {
fs.readFile("./resources/观书有感.md", (err, data) => {
// 如果失败
if (err) reject(err);
// 成功
resolve(data);
});
});
}
// 三个读取文件函数返回的都是一个Promise对象,在async函数内使用await获取Promise成功的值,不再使用then进行回调处理,将异步操作变的像同步操作
// 声明一个 async 函数
async function main() {
// 获取为学内容
let weixue = await readWeiXue();
// 获取插秧诗内容
let chayang = await readChaYangShi();
// 获取观书有感内容
let guanshu = await readGuanShuYouGan();
console.log(weixue + "\r\n" + chayang + "\r\n" + guanshu);
}
main();使用asyc 和 await 可以代替Promise的then方法,让代码看起来能加同步化
4.1.4 saync与await封装AJAX请求
用async与await去接收AJAX请求返回的结果
// 发送 AJAX 请求 返回结果为promise对象
function sedAJAX(url) {
// 返回一个promise对象 await后要跟promise对象
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方法测试
sedAJAX("").then((value) => {
console.log(value);
});
sedAJAX("").then((value) => {
console.log(value);
});
// async与 await 测试
async function main() {
// 获取数据 await能得到后面promise成功的数据
let result1 = await sedAJAX("https://api.apiopen.top/getJoke");
console.log(result1);
let reslut2 = await sedAJAX("");
console.log(reslut2);
}
main();常用:使用async await 发送 axios 请求
jsasync function getDog() { try { // result 接收请求成功返回的数据 let result = await axios.get( "https://dog.ceo/api/breed/pembroke/images/random" ); dogList.push(result.data.message); // 成功处理 } catch (error) { // 请求失败 error:错误信息 alert(error); // 失败处理 } }
4.2 对象扩展方法
4.2.1 Object.keys,Object.values 和 Object.entries
- Object.keys()方法返回一个给定对象的所有键组成的数组
// 声明一个对象
const school = {
name: "xx",
cities: ["北京", "上海", "深圳"],
xueke: ["语文", "数学", "英语"],
};
// 获取对象所有的键
console.log(Object.keys(school));
// 使用for...of 和 Object.keys()遍历对象的键
for (let key of Object.keys(school)) {
console.log(key);
}- Object.values()方法返回一个给定对象的所有值组成的数组
// 声明一个对象
const school = {
name: "xx",
cities: ["北京", "上海", "深圳"],
xueke: ["语文", "数学", "英语"],
};
// 获取对象所有的值
console.log(Object.values(school));
// 使用for...of 和 Object.values()遍历对象的值
for (let v of Object.values(school)) {
console.log(v);
}- Object.entries()方法返回一个给定对象键值组成的 [key,value] 数组
// 声明一个对象
const school = {
name: "xx",
cities: ["北京", "上海", "深圳"],
xueke: ["语文", "数学", "英语"],
};
// 获取对象所有的键值对组成的数组
console.log(Object.entries(school));
// 创建一个map对象
const m = new Map(Object.entries(school));
console.log(m);
console.log(m.get("name"));4.2.2 Object.getOwnPropertyDescriptors
该方法返回指定对象所有自身属性的描述对象
// Object.getOwnPropertyDescriptors 获取对象的描述对象
console.log(Object.getOwnPropertyDescriptors(school));
// 创建一个对象,并为其添加描述对象
const obj = Object.create(null, {
name: {
// 属性值
value: "xx",
// 属性特性
writable: true,
configurable: true,
enumerable: true,
},
});五、ES9 新特性
5.1 .Rest/Spread 属性
Rest 参数与 spread 扩展运算符在 ES6 中已经引入,不过 ES6 中只针对于数组, 在 ES9 中为对象提供了像数组一样的 rest 参数和扩展运算符
reset参数
function connecct({ host, port, ...user }) { // 对象解构获取剩余参数
console.log(host, port);
console.log(user); // 返回一个对象
}
connecct({
host: "127.0.0.1",
port: 80,
username: "root",
password: "123456",
});对象的reset参数用于获取参数为对象形式
扩展运算符
const one = {
name: "张三",
age: 18,
};
// 使用扩展运算符
// ...one => name:"张三",age:"18"
const two = {
sex: "男",
address: "北京",
};
const three = {
phone: "13888888888",
asy() {
console.log("您好呀");
},
};
// 使用扩展运算符合并对象
const user = { ...one, ...two, ...three };
console.log(user);对象的扩展运算符可用于对象的合并
5.2 正则表达式命名捕获分组
ES9 允许命名捕获组使用符号『?』,这样获取捕获结果可读性更强
let str = '<a href="http://www.atguigu.com">尚硅谷</a>';
// 提取 url和标签文本 正则匹配
// 使用 (?<name>正则表达式) 进行分组
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/;
const result = reg.exec(str);
console.log(result.groups);
console.log(result.groups.url, result.groups.text);5.3 正则表达式反向断言
ES9 支持反向断言,通过对匹配结果前面的内容进行判断,对匹配进行筛选。
正向断言匹配结果后面的内容进行判断
// 字符串
let str = "JS12345嘿嘿6789啦啦";
// 提取6789
// 正向断言 匹配结果后面的内容进行判断
const reg = /\d+(?=啦)/;
// \d+(?=啦)匹配以"啦"结尾的数字字符串,但不会将 "啦" 作为匹配结果返回
const result = reg.exec(str);
console.log(result);
// 反向断言
const reg2 = /(?<=嘿)\d+/;
//(?<=嘿)\d+ 匹配以嘿开头的数字字符串,但不会将 "啦" 作为匹配结果返回
const result2 = reg2.exec(str);
console.log(result2);5.4 正则表达式 dotAll 模式
正则表达式中点.匹配除回车外的任何单字符,标记『s』改变这种行为,允许行终止符出现
// 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;
// 执行匹配
let result;
let data = [];
while ((result = reg.exec(str))) {
data.push({ title: result[1], time: result[2] });
}
// 输出结果
console.log(data);5.5 正则补充


六、ES10 新特性
6.1.Object.fromEntries
接收一个二维数组或者一个Map创建一个对象
与 Object.entries 将对象转换为二维数组
Object.entries 与 Object.fromEntries 互为逆运算
// 二维数组
const result = Object.fromEntries([
["name", "张三"],
["age", "18"],
["tel", "9892068902"],
]);
console.log(result);
// Map
const m = new Map([
["age", "18"],
["tel", "9892068902"],
]);
m.set("name", "张三");
const result1 = Object.fromEntries(m);
console.log(result1);
// Object.entries() 将对象转换为二维数组
const arr = Object.entries(result);
console.log(arr);6.2.trimStart 和 trimEnd
ES5 trim:清除字符串两侧的空白字符
ES10 trimStart trimEnd 指定清除字符串左侧或右侧的空白字符
console.log(str);
console.log(str.trim()); // 清除字符串两侧空白
console.log(str.trimStart()); // 清除字符串左侧空白
console.log(str.trimEnd()); // 清除字符串右侧空白6.3.Array.prototype.flat 与 flatMap
6.3.1 flat方法
将多维数组的维度降低
arr.flat([n])
可以传递参数,是一个数字,默认值为1
const arr = [
[1, 2],
[3, 4],
];
// 二维数组转一维数组
console.log(arr.flat()); // [1,2,3,4]
const arr2 = [
[1, 2, [5, 6]],
[3, 4],
];
// 三维数组转二维数组
console.log(arr2.flat()); //[1, 2, Array(2), 3, 4]
// 三维数组转一维数组
console.log(arr2.flat(2)); //[1, 2, 5, 6, 3, 4]6.3.2 flatMap方法
将Map返回数组结果维度降低
const arr3 = [1, 2, 3, 4];
const result = arr3.flatMap((item) => item * 10);
console.log(result); //[10, 20, 30, 40]6.4.Symbol.prototype.description
获取symbol的字符串描述
let s = Symbol('嘿嘿');
console.log(s.description); // 嘿嘿七、 ES11 新特性
7.1.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>/gs;
// 调用方法
const result = str.matchAll(reg);
console.log(result); // 返回结果为一个可迭代对象
for (let v of result) {
console.log(v);
}批量获取数据
7.2.类的私有属性
- 使用 # 声明私有属性
- 私有属性在类的外不能直接被访问
- 可以通过constructor进行赋值
- 私有属性不能被继承
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, "50kg");
console.log(girl);
console.log(girl.name);
// console.log(girl.#age); 私有属性在类的外部能直接访问
girl.intro(); // 通过在类的内部封装方法进行访问7.3.Promise.allSettled 和 Promise.all
7.3.1 Promise.allSettled
- 接收一个Promise数组返回一个Promise对象
- 返回结果永远为成功状态,成功的状态的值为每一个Promise对象状态和值
作用:批量处理Promise异步任务
// 声明两个Promise对象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("商品数据 - 1 ");
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("商品数据 - 2 ");
reject("出错了");
}, 1000);
});
// 调用 allsettled方法
const reslut = Promise.allSettled([p1, p2]);
console.log(reslut);
// 无论接收的Promise对象是否为成功状态,返回的Promise都为成功
reslut.then((value) => {
for (v of value) {
console.log(v);
}
});7.3.2 Promise.all
- 接收一个Promise数组,返回一个Promise对象
- 当全部的Promise状态为成功返回状态为成功,有一个状态为失败,返回状态为失败
// 声明两个Promise对象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("商品数据 - 1 ");
}, 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve("商品数据 - 2 ");
reject("出错了");
}, 1000);
});
const res = Promise.all([p1, p2]);
res
.then((value) => {
console.log(value);
})
.catch((err) => {
console.log(err);
});7.4.可选链操作符
可选链操作符 ?.
使用可选链可避免对象参数层层判断
function main(config) {
// 获取db的host属性
// 先判断是否传入了值 避免报错
// const dbHost = config && config.db && config.db.host;
// 使用可选链操作符可以避免层层判断
const dbHost = config?.db?.host;
console.log(config.db.host);
}
main({
db: {
host: "192.168.1.1",
username: "root",
},
cache: {
host: "192.168.1.2",
user: "admin",
},
});7.5.动态 import 导入
ES6的模块化是一个静态的模块化系统
动态 import 在需要时进行导入(用于路由懒加载)
语法:import(url)返回一个promise对象
// 静态引入
// import { hello } from "./hello.js";
// 获取元素
const btn = document.getElementById("btn");
btn.onclick = function () {
// hello(); 使用
// 动态引入 返回一个promise对象
import('./hello.js').then(module => {
console.log(module);
module.hello();
});
};7.6 BigInt类型
- 大整型,在数字后面加上n
- 用于更大数的运算
- 只能和BigInt类型的数进行运算
// 大整型
let n = 123n;
console.log(n, typeof n); // 123n 'bigint'
// 函数 将普通的整数转换为大整型
let n1 = 123;
console.log(BigInt(n1)); //123n
// 作用:用于大数值的运算
let max = Number.MAX_SAFE_INTEGER; // 最大值
console.log(max);
console.log(max + 1);
console.log(max + 2); // 超出范围计算错误
console.log(BigInt(max)); // 借助BigInt计算
console.log(BigInt(max) + BigInt(1));
console.log(BigInt(max) + BigInt(10));7.7 globalThis 对象
始终指向全局对象
在浏览器下的全局对象是window
在nodeJS下的全局对象是global