ES6 新特性
1,let变量声明
-
作用:
- 与var类似, 用于声明一个变量
-
特点:
- 在块作用域内有效
- 不能重复声明
- 不会预处理, 不存在提升
-
应用:
-
循环遍历加监听
-
使用let取代var是趋势
-
<script type="text/javascript">
//console.log(age);// age is not defined
// 声明变量方式
let age = 12;
// 1,变量不能重复声明,但是使用var是可以的,为了防止变量重名
let str = '罗志祥';
let str = '小猪' //这个会报错
// 2,块级作用域
// let只在代码块中有效,除了{}之外,还有if else while for中国
{
let name= 'XXX' //若使用var来定义,下面是可以读取到值的
}
console.log(name) //这个地方是读取不到的
// 3,不存在变量提升
console.log(song) //会报错,这是不被允许的,但是var变量是可以进行变量提升的
let song='恋爱达人'
//4,不影响作用域链
{
let school='sss'
function fn(){
console.log(school); //是可以进行输出的
} // 循环体中对比
let btns = document.getElementsByTagName('button');
for(let i = 0;i<btns.length;i++){
btns[i].onclick = function () {
// 如果使用var来定义i的时候,循环之后i就会变成3,没有块作用域的一个概念
// 但是使用let进行定义i的时候,就会有块作用域的概念,这个i的值就是动态的
this.style.backgroundColor='pink'
btns[i].style.backgroundColor='pink'
}
}
// 上面的代码就被分解为
var i=0 //循环3次之后这里的全局变量i是等于3的
{
// 当回调函数被点击的时候,就去上一个作用域里面去找,结果得到的i就是3
// 为了避免这个情况,最好使用let来声明变量
btns[i].onclick = function () {
this.style.backgroundColor='pink'
btns[i].style.backgroundColor='pink'
}
}
}
</script>
2,const
-
作用:
- 定义一个常量
-
特点:
- 不能修改
- 其它特点同let
-
应用:
- 保存不用改变的数据
-
可以用来声明常量和数组
<script type="text/javascript">
// 声明常量
const sex = '男';
console.log(sex);
// 1,一定要赋初始值,下面的情况是不允许的
const sex;
// 2,一般常量使用大写
const SEX='XXX'
// 3,常量的值不能修改
sex = '女';//不能修改
// 4,和let一样是块级作用域
// 5,对于数组对象元素的修改,不算做对常量的修改,不会报错
// 原因是,从const声明的变量保存的地址值没有改变,那就没有报错
const TEAN=['ABC','ERT','BUH']
TEAN.push('MGH')
</script>
3,变量的解构赋值
- 理解:
- 从对象或数组中提取数据, 并赋值给变量(多个)
- 对象的解构赋值
- let {n, a} = {n:‘tom’, a:12}
- 数组的解构赋值
- let [a,b] = [1, ‘atguigu’];
- 用途
* 给多个形参赋值
<script type="text/javascript">
// 对象的解构赋值(常用,一般用于对象的方法调用过多时,为了节省代码量)
let obj = {name : 'kobe', age : 39};
// 一般写法
let name = obj.name;
let age = obj.age;
console.log(name, age);
// 解构赋值写法
let {name,age} = obj; //相当于声明的两个变量分别赋值为对象的值
let{name}=obj //这种形式也是可以的
console.log(name) //输出 kobe
console.log(age); //输出的是39
//1,不用解构赋值时的缺点案例
function person(p) {
console.log(p.name, p.age);//不用时,要这样调用
}
person(obj);
function person1({name, age}) {
console.log(name, age);//用了之后,可以这样调用
}
person1(obj);
// 2,不用解构赋值时的缺点案例
obj1={
name:'hhh',
age=23,
setName= function(name){
this.name=name;
}
}
obj1.setName('uuu') //不用时需要这样写
let setName = obj
setName(name) //用时可以直接写,省略了很多代码量
//数组的解构赋值 不经常用
let arr = ['abc', 23, true];
let [a, b, c, d] = arr;
console.log(a, b, c, d);
</script>
4,模板字符串
(1)声明:
ES6 引入新的声明字符串的方式 ,反引号 ``,常用的符号有’ '," ",单引和双引
(2)内容中可以直接出现换行符,在单引和双引是不可以的
-
模板字符串 : 简化字符串的拼接
-
模板字符串必须用 `` 包含
-
变化的部分使用${xxx}定义
-
// 1,内容中可以直接出现换行符
// 在单引和双引中是不能有换行符的,会报错,要用+进行连接才可以
let str ='<ul>
<li>
玛丽
</li>
</ul>'
// 对于反引号来说,直接有换行符,是可以的
let str =`<ul>
<li>
玛丽
</li>
</ul>`
// 2,变量拼接
let loveset='周杰伦'
let out=`${loveset}是我最喜欢的歌手`;//这样就可以不在用+进行拼接了。
5,简化对象的写法
- 省略同名的属性值
- 省略方法的function
<script type="text/javascript">
let x = 3;
let y = 5;
普通额写法
let obj = {
x : x,
y : y,
getPoint : function () {
return this.x + this.y
}
};
//简化的写法
let obj = {
x,
y,
getPoint(){
return this.x
}
};
console.log(obj, obj.getPoint());
</script>
6,箭头函数
-
作用: 定义匿名函数
-
基本语法:
- 没有参数: () => console.log(‘xxxx’)
- 一个参数: i => i+2
- 大于一个参数: (i,j) => i+j
- 函数体不用大括号: 默认返回结果
- 函数体如果有多个语句, 需要用{}包围,若有需要返回的内容,需要手动返回
-
使用场景: 多用来定义回调函数
-
箭头函数的特点:
1、简洁
2、箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
3、扩展理解: 箭头函数的this看外层的是否有函数,
如果有,外层函数的this就是内部箭头函数的this,
如果没有,则this是window。
<script type="text/javascript">
// 1,声明一个函数
let fun = function () {
console.log('fun()');
};
//没有形参,并且函数体只有一条语句时可以省略花括号
let fun1 = () => {
console.log('fun1()');
}
let fun1 = () => console.log('fun1()')
//只要一个形参,并且函数体只有一条语句
let fun2 = x => x;
console.log(fun2(5));
//形参是一个以上
let fun3 = (x, y) => x + y;
console.log(fun3(25, 39));//64
//函数体有多条语句
let fun4 = (x, y) => {
console.log(x, y);
return x + y;
};
console.log(fun4(34, 48));//82
// 定时器函数
setTimeout(() => {
console.log(this);
},1000)
// 点击事件
let btn = document.getElementById('btn');
//没有箭头函数
btn.onclick = function () {
console.log(this);//btn
};
//使用箭头函数
let btn2 = document.getElementById('btn2');
btn.click=()=>{
console.log(this);//btn
}
// 2,this是静态的,this始终指向函数所在声明时所在作用域下的this的值
// 无论是谁调用箭头函数,这个函数的this值都不会发生改变
function getName(){
console.log(this.name)
}
let getName2=()=>{
console.log(this.name)
}
// 设置window对象的name属性
window.name='window'
const school ={
name:'内部方法'
}
// 直接调用,都是window来调用的
getName(); //这里的this也是window
getName2(); //这里的this也是window
// call方法调用
getName.call(school); //这里的this就是school
getName2.call(school); //这里的this依旧还是window
// 3,不能作为构造函数,实例化对象
function Fn(){
this.name=name;
}
//这里可以根据构造函数Fn,来去实例化一个对象
let fn=new Fn()
// 箭头函数,不能作为构造函数去实例化一个对象
let Fn = ()=>{
}
// 4,不能使用arguments对象
// 5,箭头函数的实践1,点击div 2s后颜色变化
// 获取元素
let ad=document.getElementById('ad')
// 绑定事件
// 一般想在定时器中用this进行操作,就会在定时器的外部设置一个变量绑定元素
ad.addEventListener('click',function(){
let _this=this
setTimeout(function(){
_this.style.background='pink'
},2000)
// 可以用箭头函数来实现这种操作,因为这是箭头函数的this就是ad这个元素
ad.addEventListener('click',function(){
let _this=this
setTimeout(()=>{
_this.style.background='pink'
},2000)
})
// 6,箭头函数的实践2 从数组中返回偶数的元素
// 正常做法
const arr=[1,4,6,3,2]
const res =arr.filter(function(item){
if(item % 2===0){
return true;
}else{
return false;
}
})
// 使用箭头函数
const res= arr.filter(item => item %2 ===0)
// 7,箭头函数适合场景
// 适合与this无关的问题,比如定时器,数组的方法回调
// 不适合与this有关的回调,事件回调,对象的方法
</script>
7,参数默认初始值
ES6允许给函数参数赋值初始值
-
1,形参初始值 具有默认值的参数 一般位置要靠后
-
2,可以和解构赋值相联系
<script type="text/javascript">
// 1,形参初始值 具有默认值的参数 一般位置要靠后
//定义一个点的坐标
function Point(x, y=12) {
this.x = x;
this.y = y;
}
// 2,与解构赋值相结合
function connect({host,username,password=root}){
console.log(host)
console.log(username)
}
connect({
host:'localhost',
username:'root'
})
</script>
8,rest参数
- es6引入rest参数,用于获取函数的实参,用来代替arguments
- rest参数只能放在最后。
<script type="text/javascript">
function fun(...values) {
// es5用法
// 参数是(arguments),伪数组;
// es6用法
console.log(values);
values.forEach(function (item, index) {
console.log(item, index);
})
}
fun(1,2,3);
// rest参数必须要放到最后
function fn(a,b,...argus){
console.log(a)
console.log(b)
console.log(args)
}
fn(2,3,4,5,6)
// 输出的是,a:2,b:3,args:[4,5,6]
</script>
9,spread扩展运算符
[…]扩展运算符能将数组转换为逗号分割的【参数序列】
// ...扩展运算符
// 声明一个数组
const boy=['123','345']
// 声明一个函数
function fn(){
// 得到的是一个数组['123','345']
console.log(arguments)
// 得到的就是两个参数,0:'123',1:'345'
// 就等同于把参数序列分开进行传参,就相当于把数组装换为参数列表进行传参
console.log(...boy)
}
// 将数组输入到函数中
fn(boy)
- 这里和rest参数不同的是,rest参数是写在定义函数体参数的括号里面的
- 而这个扩展运算符是放在调用方法时的参数的括号里面的
…运算符的应用:
// 1,数组的合并
const a=['123','234']
const b=['78','689']
//传统的将两个数组进行合并
const c=a.concat(b)
// 使用扩展运算符进行合并
const c=[...a,...b]
// 2,数组的克隆
const d=['1','2']
const e=[...d] //如果d中有引用类型的时候,是一个浅拷贝
// 3,将伪数组转成真正的数组
const divs=document.querySelectorAll('div')//得到的是一个伪数组
const Divarr = [...divs];//得到的变量是一个伪数组
10,symbol
-
概念:
-
ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
-
特点:
1、Symbol属性对应的值是唯一的,解决命名冲突问题
2、Symbol值不能与其他数据进行计算,包括同字符串拼串
3、for in, for of遍历时不会遍历symbol属性。 -
使用:
1、调用Symbol函数得到symbol值
let symbol = Symbol();
let obj = {};
obj[symbol] = ‘hello’;
2、传参标识
let symbol = Symbol(‘one’);
let symbol2 = Symbol(‘two’);
console.log(symbol);// Symbol(‘one’)
console.log(symbol2);// Symbol(‘two’)
3、内置Symbol值-
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语
-
内部使用的方法。
-
Symbol.iterator
* 对象的Symbol.iterator属性,指向该对象的默认遍历器方法(后边讲)
-
-
<script type="text/javascript">
window.onload = function () {
// 创建symbol的方式1
// symbol属性对应的值是唯一的,解决命名冲突问题
let symbol = Symbol();
console.log(typeof symbol);
console.log(symbol);
let symbol2=Symbol('尚硅谷')
let symbol3=Symbol('尚硅谷')
console.log(symbol2 === symbol3) //返回的是false
// 创建symbol的方式2
let s4=Symbol.for('字符串')
let s5=Symbol.for('字符串')
let s6=Symbol.for('字符串')
console.log(s5 === s6) //返回的是true
// 用作对象的属性(唯一)
let obj = {username: 'kobe', age: 39};
obj[symbol] = 'hello';
obj[symbol] = 'symbol';
console.log(obj);
for(let i in obj){
console.log(i);
}
}
// 不能与其他数据进行运算,对比和拼接都不可以
</script>
<script>
// symbol的使用,它对应的值是唯一的,可以向对象中添加方法和属性
// 当对象的结构很复杂的时候,我们没有办法很容易的查看内部的属性和方法,为了避免命名冲突
// 我们就可以使用symbol进行添加属性
// 1,添加的方式1
// 一个未知的对象
let game={
}
// 声明另一个对象
let methods={
up:Symbol(),
down:Symbol()
}
// 将值赋值给game中
game[methods.up]=function(){
console.log('123')
}
game[methods.down]=function(){
console.log('345')
}
// 2,添加的方式2
let youxi= {
name:'狼人杀',
[Symbol('say')]:function(){
console.log('我可以发言')
}
}
</script>
11,Symbol内置值
12,迭代器
-
*概念:
-
iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制,任数据结构只要部署了iterator接口,就可以完成遍历操作,
-
是Symbol的一个属性
-
作用
1、为各种数据结构,提供一个统一的、简便的访问接口;
2、使得数据结构的成员能够按某种次序排列
3、ES6创造了一种新的遍历命令for…of循环,Iterator接口主要供for…of消费。
-
工作原理
- 创建一个指针对象,指向数据结构的起始位置。
- 第一次调用next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
- 每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
** value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。*
** 当遍历结束的时候返回的value值是undefined,done值为false*
-
原生具备iterator接口的数据(可用for of遍历)
-
1、Array
2、arguments
3、set容器
4、map容器
5、String
-
迭代器的使用:
const xiyou =['12','34','45']
// 使用for..of循环数组
for(let v of xiyou ){
console.log(v) //直接是遍历的键值
}
for (let i in xiyou ){
console.log(i) //这个遍历的是索引号
}
自定义迭代器案例:
// 自定义迭代器遍历对象
// 需求:在使用for..of的时候,返回的是stus数组里面的值
// 声明一个对象
const banji= {
name:'终极一班',
stus:[
'123',
'234',
'344',
'344'
],
// 自定义迭代器,是根据迭代器的原理来写de
[Symbol.iterator](){
let index=0;
let _this=this
return {
next:function(){
if(index<_this.stus.length){
return {
value:this.stus[index],
done:false
}
// 下标自增
index++
}else{
return{value:undefined,done:true}
}
}
}
}
}
13,生成器
-
概念:(就是一个函数)
- 1、ES6提供的解决异步编程的方案之一
- 2、Generator函数是一个状态机,内部封装了不同状态的数据,
- 3、用来生成遍历器对象
- 4、可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
-
特点:
-
1、function 与函数名之间有一个星号
-
2、内部用yield表达式来定义不同的状态
-
例如:
*function\* generatorExample(){*
*let result = yield 'hello'; // 状态值为hello*
*yield 'generator'; // 状态值为generator*
*}*
-
3、generator函数返回的是指针对象(接11章节里iterator),而不会执行函数内部逻辑
-
4、调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
-
5、再次调用next方法会从上一次停止时的yield处开始,直到最后
-
6、yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
<script type="text/javascript">
// 1,声明方式
function * gen(){
console.log('函数开始执行');
}
//2,执行方式
// 调用方式是不同的,这里不会直接打印输出函数开始执行
let generator = gen()
// 调用下面的方法才能打印输出
generator.next()
// 3,可以在函数体内加上yield语句
// yield是作为函数代码的分隔符,是根据next方法来决定执行
function* generatorTest() {
console.log('函数开始执行');
yield 'hello';
console.log('函数暂停后再次启动');
yield 'generator';
}
// 执行方式
// 生成遍历器对象
let Gt = generatorTest();
// 执行函数,遇到yield后即暂停
// next()方法执行后,可返回一个值,这个值是yield后面的值和done属性
console.log(Gt); // 遍历器对象
let result = Gt.next(); // 函数执行,遇到yield暂停
console.log(result); // {value: "hello", done: false}
result = Gt.next(); // 函数再次启动
console.log(result); // {value: 'generator', done: false}
result = Gt.next();
console.log(result); // {value: undefined, done: true}表示函数内部状态已经遍历完毕
// Gt是一个遍历器对象,可以通过for of来进行遍历
for (let v of Gt){
console.log(v) //这时候就会一个一个进行输出
}
// 4,生成器函数参数
function * gen(args){
console.log(args);
let one = yield 111;
console.log(one);
yield 222;
yield 333;
}
// 执行获取迭代器对象
let iterator = gen('AAA');
iterator.next() //当执行第一个yeild时,就会打印出AAA这个参数,所以在生成器函数上加上参数是没问题的
// next()方法也传入实参
// next()传入的参数是作为上一条yeild返回的结果
iterator.next('BBB');
// 5,生成器函数实例
// 异步编程 文件操作,网络操作(ajax,request)数据库操作
// 定时器的操作案例,需求:1s后在控制台输出 111,然后2s后在控制台输出 222 然后3s后输出333
// 一般做法,就是定时器里面套定时器,但当异步任务过多的时候,回调太多,就形成一个回调地狱
//使用生成器函数来做这个效果
function one(){
setTimeout(()=>{
console.log(111);
iterator.next(); //为了接着执行
},1000)
}
function two(){
setTimeout(()=>{
console.log(222);
iterator.next();
},1000)
}
function three(){
setTimeout(()=>{
console.log(333);
iterator.next();
},1000)
}
function * gen(){
yield one();
yield two();
yield three();
}
// 调用生成器函数
let iterator = gen();
iterator.next();//调用gen()第一次
// 6,生成器函数实例2
// 模拟获取 用户数据 订单数据 商品数据
function getUser(){
setTimeout(()=>{
let data ='用户数据'
iterator.next(data) //data将作为第一次yield返回的结果
},1000)
}
function getOrder(){
setTimeout(() => {
let data='订单数据'
iterator.next(data)
}, 1000);
}
function getGoods(){
setTimeout(() => {
let goods='订单数据'
iterator.next(data)
}, 1000);
}
function * gen(){
let userDate = yield getUser()
console(userDate) //会输出 '用户数据'
yield getOrder()
yield getGoods()
}
14,promise
-
概念:
- Promise对象: 代表了未来某个将要发生的事件(通常是一个异步操作)
- promise是ES6引入的异步编程的新解决方案。是用来解决回调地狱的解决方法。语法上promise是一个构造函数。
-
使用步骤
* 创建promise对象 let promise = new Promise((resolve, reject) => { //初始化promise状态为 pending //执行异步操作 if(异步操作成功) { resolve(value);//修改promise的状态为fullfilled } else { reject(errMsg);//修改promise的状态为rejected } }) * 调用promise的then() promise.then(function( result => console.log(result), errorMsg => alert(errorMsg) ))
-
对象的三个状态
- ** pending: 初始化状态**
- *** fullfilled: 成功状态***
- ** ** rejected: 失败状态*
// 1,promise对象的介绍与基本使用
// 实例化Promise对象
const p = new Promise(function(resolve,reject){
setTimeout(function(){
// 模拟一个数据
let data ='数据库中的用户数据'
// 调用reslove方法,这个p对象的状态就会变成出初始化状态pending
resolve(data)
// 调用reject方法,这个p对象就会变成失败状态
},1000)
})
// 初始化状态时,调用promise 的then方法的第一个函数参数
// 失败状态时,自动调用promise的then方法的第二个函数参数
p.then(function(value){},function(reason){})
// 2,promise读取文件
const p=new Promise(function(resolve,reject){
fs.readFile("./resources/为学.md",(err,data)=>{
// 判断如果失败,直接就调用p.then的第二个参数函数
if(err) reject(err)
// 如果成功,直接就调用p.then的第一个参数函数
resolve(data)
})
});
p.then(function(value){},function(reason){})
// 3,promise封装ajax
// 普通操作
// 1,创建对象
const xhr = new XMLHttpRequest()
// 2,初始化
xhr.open("GET","https://api.apiopen.top/getJoke")
// 3,发送
xhr.send();
// 4,绑定事件,处理响应结果
xhr.onreadystatechange = function(){
// 判断
if(xhr.readyState === 4){
// 判断响应状态码 200-299
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response);
}else{
// 如果失败
console.error(xhr.status);
}
}
}
// 使用promise来写
const p=new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.open("GET","https://api.apiopen.top/getJoke")
xhr.send();
xhr.onreadystatechange = function(){
// 判断
if(xhr.readyState === 4){
// 判断响应状态码 200-299
// 如果是,就代表成功,就调用reslove函数,修改promise的状态码
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response);
}else{
// 如果失败,就调用reject函数,修改promise的状态码
reject(xhr.status);
}
}
}
})
// 指定promise函数的成功和失败的回调
p.then(function(value){
console.log(value)
},function(reason){
console.log(reason)
})
</script>
15,promise的then方法
关于promise的then方法
<script>
// 创建promise对象
const p = new PromiseRejectionEvent((resolve,reject)=>{
setTimeout(()=>{
resolve('用户数据');//设定一个成功的状态
reject('出错啦') // 设定一个失败的状态
})
})
// 调用 then 方法,then方法的返回结果是promise对象,对象状态由回调函数的执行结果决定
// 1,如果回调函数中返回的结果是 非promise类型的属性,状态是成功,返回值为对象的成功的值
// 2,如果回调函数中返回的结果是一个promise对象,那这个返回的promise对象的状态就是p.then的状态
const p=p.then(value=>{
console.log(value);
// 1,返回的是非promise类型的属性
return '123'
// 2,返回的是promise对象
return new Promise((resolve,reject)=>{
resolve('ok')
})
// 3,抛出错误
throw new Error('出错') //那状态就是失败的状态,值就是'出错'
},reason =>{})
// then方法可以进行链式调用,一层一层去调用,避免回调地狱的情况
p.then(value=>{},reason=>{}).then(value=>{},reason=>{})
</script>
16,promise的实践—读取多个文件
// promise实践---读取多个文件
// 类似的情况就是,再返回数据得时候,比如用ajax返回用户id,然后再根据用户id查询其它的数据
// 这种情况就是一层一层的套,这就是回调地狱
// 普通做法:
// 1,引入 fs模块
const fs = require('fs');
const { resolve } = require('path/posix');
const { resourceLimits } = require('worker_threads');
// 2,调用方法读取文件
fs.readFile('js/jquery-1.10.1.min1.js',(err,data1)=>{
fs.readFile('js/jquery-1.10.1.min2.js',(err,data2)=>{
fs.readFile('js/jquery-1.10.1.min3.js',(err,data3)=>{
})
})
})
// 使用promise 实现
const p = new Promise((resolve,reject)=>{
fs.readFile('路径1',(err,data)=>{
resolve(data);
})
})
p.then(value=>{
console.log(value) //这里的value的值就是data
// 为了再进行一次读取文件,就让其返回一个promise对象
return new Promise(value=>{
fs.readFile('路径2',(err,data)=>{
// value是一个文件里的内容,data是第二个文件里面的内容,可以将其作为一个文件的合并
// 这里返回的promise的值就是p.then返回的值,并且状态也是一样的
resolve([value,data])
})
})
}).then(value =>{
// 这里的value就是上一个返回的[value,data]
fs.readFile('路径3',(err,data)=>{
// 压入
value.push(data)
resolve(value)
})
}).then(value =>{
// 这里的value就是三个文件中的内容
console.log(value)
})
17,promise --catch方法
<script>
// promised的catch方法
// 就是设置一个失败回调的方法
const p = new Promise((resolve,reject)=>{
setTimeout(() => {
// 设置p对象的状态为失败,并设置失败的值
reject('出错啦')
}, 1000);
})
// 1,使用p.then方法
p.then(function(value){},function(reason){
console.error(reason)
})
// 2,使用catch方法
p.catch(function(reason){
console.warn(reason);
})
</script>
18,set
新的数据结构set(集合),类似于数组,但成员都是唯一的,集合实现了iterrator接口,所以可以使用【扩展运算符】和【for…of】进行遍历
- 容器: 能保存多个数据的对象, 同时必须具备操作内部数据的方法
- 任意对象都可以作为容器使用, 但有的对象不太适合作为容器使用(如函数)
set的特点:保存多个value, value是不重复 ====>数组元素去重
API:
- Set()/Set(arr) //arr是一维数组
- add(value) //往set添加一个元素
- delete(value) //从set里删除一个元素
- clear(); //清除set里的内容
- has(value) // 确定set里面有没有某个元素
- size // 返回
<script>
let s=new Set()
let s1=new Set(['大事','小事'])
// 实践
let arr =[1,2,3,42,2,4];
// 1,数组去重
// 先将arr的内容存进set,因为set是一个不重复的数组
// 然后再将set装换成数组
let res=[...new Set(arr)]
// 2,交集
let arr2=[4,6,2,7,4];
// filter是数组的遍历结果
let res=[...new Set(arr)].filter(item =>{
let s2 = new Set(arr2);//s2是arr2去重后的数组
if(s2.has(item)){
return true
}else{
return false
}
})
// 3,并集
let union =[...arr,...arr2];//先将两个数组进行合并
let res=[...new Set(union)];//然后设置为set,自动去重
// 4,差集
let diff=[...new Set(arr).filter(item =>! (new Set(arr2).has(item)))];
</script>
19 ,Map
- Map数据结构,类似于对象,也是键值对的集合。但是键的范围不局限于字符串,各种类型的值(包括对象都可以当做键)。
- Map也实现了iterator接口,座椅可以使用扩展运算符和【for…of】进行遍历。
- map的特点: 保存多个key–value, key是不重复, value是可以重复的
API:
- Map()/Map(arr) //arr是二维数组
- set(key, value)
- delete(key)
- clear()
- has(key)
- size
<script>
// 声明map
let m= new Map();
// 添加元素
m.set('name','wlijun');
m.set('change',function(){
console.log('change you');
})
let key={
school:'ncut'
}
m.set(key,['北京'])
// 删除
m.delete('name')
// 查看
console.log(m.get(key));
// 清空
// m.clear
// 遍历
for(let v of m){
console.log(v)
}
</script>
20,class类
通过class关键字,可以定义类,用来实例化一个对象。
对于class能做到的东西,es5都可以做到,新的class的写法只是让对象原型的写法更加清晰,更像面向对象变成的语法而已。
(1)class声明类
(2)constructor定义构造函数初始化
class Person {
//调用类的构造方法,constructor 不能修改
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法,必须要这样定义
showName(){
console.log(this.name, this.age);
}
}
let person = new Person('kobe', 39);
console.log(person, person.showName());
(3)extends继承父类
(4)super调用父类构造方法
// 继承
// 举例es5
// 父类
function Phone(brand,price){
this.brand=brand;
this.price=price;
}
Phone.prototype.callOther = function(){
console.log('我可以打电话')
}
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price);
this.color = color;
this.size = size;
}
// 设置自己构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor =SmartPhone;
// 声明子类的方法
SmartPhone.prototype.photo = function(){
console.log('我可以拍照')
}
SmartPhone.prototype.play=function(){
console.log('我可以玩')
}
// 实例化子类的实例对象
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5');
// 里面是有父类的方法的
console.log(chuizi)
// class类的方法
// 父类
class Person {
//调用类的构造方法,constructor 不能修改
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法,必须要这样定义
showName(){
console.log(this.name, this.age);
}
}
//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){
super(name, age);//调用父类的构造方法
this.salary = salary;
}
showName(){//在子类自身定义方法
console.log(this.name, this.age, this.salary);
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();
(5)static定义静态方法和属性
// 静态成员
// 举例,用es5
function Phone(){
}
// 这里的属性和方法是属于函数的,被称为静态成员
Phone.name= '手机'
Phone.change=function(){
console.log('...')
}
Phone.prototype.size='123';
// 这里的ip实例对象是不能调用构造函数的name属性和change方法的
// 实例对象和函数对象的属性和方法是不相通的。
// 这里的实例对象是和构造函数Phone的ProtoType上的属性是相通的。
let ip = new Phone();
// 这两个是不可调用的
// ip.change()
// let name=ip.name
// 下面的这个是可以调用的
ip.size();
// class的静态属性
class shouji{
// 静态属性
// 用static声明的属性和变量只属于类,而不属于实例对象
static name ='手机'
static change(){
console.log('改变世界');
}
}
(6)父类方法可以重写
子类可以重写父类的方法
比如父类有一个方法是call(),那父类就可以重新定义这个call() 方法
// class类的方法
// 父类
class Person {
//调用类的构造方法,constructor 不能修改
constructor(name, age){
this.name = name;
this.age = age;
}
//定义一般的方法,必须要这样定义
showName(){
console.log(this.name, this.age);
}
}
//定义一个子类
class StrPerson extends Person{
constructor(name, age, salary){
super(name, age);//调用父类的构造方法
this.salary = salary;
}
showName(){//在子类自身定义方法
console.log(this.name, this.age, this.salary);
}
// 重写父类的同名方法,当子类的实例对象调用call方法时,就是调用自身重写的方法
// 这样就没有办法去调用父类的同名方法了
call(){
console.log('我是重写的方法');
}
}
let str = new StrPerson('weide', 38, 1000000000);
console.log(str);
str.showName();
19,数值扩展
<!--
0,Number.EPSILON是js表示 的最小精读,使用在浮点数运算上面
1. 二进制与八进制数值表示法: 二进制用0b, 八进制用0o
2. Number.isFinite(i) : 判断是否是有限大的数
3. Number.isNaN(i) : 判断是否是NaN
4. Number.isInteger(i) : 判断是否是整数
5. Number.parseInt(str) : 将字符串转换为对应的数值
6. Math.trunc(i) : 直接去除小数部分
-->
<script type="text/javascript">
// Number.EPSILON
// 对于浮点数的运算 0.2+0.1是不完全等于0.3的,所以在我们处理的时候,就可以封装一个函数
function equal(a,b){
if(Math.ads(a-b) <Number.EPSILON){
return true
}else{
return false
}
}
console.log(equal(0.1+0.2,0.3))//则返回true
// 二进制和八进制
console.log(0b1010);//10
console.log(0o56);//46
//Number.isFinite(i) : 判断是否是有限大的数
console.log(Number.isFinite(NaN));//false
console.log(Number.isFinite(5));//true
//Number.isNaN(i) : 判断是否是NaN
console.log(Number.isNaN(NaN));//true
console.log(Number.isNaN(5));//falsse
//Number.isInteger(i) : 判断是否是整数
console.log(Number.isInteger(5.23));//false
console.log(Number.isInteger(5.0));//true
console.log(Number.isInteger(5));//true
//Number.parseInt(str) : 将字符串转换为对应的数值
console.log(Number.parseInt('123abc'));//123
console.log(Number.parseInt('a123abc'));//NaN
// Math.trunc(i) : 直接去除小数部分
console.log(Math.trunc(13.123));//13
</script>
20,对象扩展
1,Object.is 判断两个值是否完全相等
2,Object.assign 对象的合并
3,Object.setPrototypeOf Object.getPrototypeof 设置原型对象
<script type="text/javascript">
// Object.is 判断两个值是否完全相等,它的功能和==类似,但也有区别
console.log(Object.is('abc', 'abc'));//true
console.log(NaN == NaN);//false
console.log(Object.is(NaN, NaN));//true
console.log(0 == -0);//true
console.log(Object.is(0, -0));//false
//Object.assign(target, source1, source2..)
let obj = {name : 'kobe', age : 39, c: {d: 2}};
let obj1 = {};
// 意思就是后面的把前面的进行覆盖
// 覆盖的是什么呢,是相同属性名会覆盖值,如果没有相同的属性名,那将不会覆盖
Object.assign(obj1, obj);
console.log(obj1, obj1.name);
//直接操作 __proto__ 属性
let obj3 = {name : 'anverson', age : 41};
let obj4 = {};
obj4.__proto__ = obj3;
console.log(obj4, obj4.name, obj4.age);
// 设置原型对象 Object.setPrototypeof(s1,s2),设置s1的原型对象是s2
const school = {
name:'尚硅谷'
}
const cities ={
xiaoqu:['北京']
}
Object.setPrototypeOf(school,cities);
console.log(school)
</script>
21,模块化
模块化就是将一个大的程序文件,拆分成许多小的文件,然后将小的文件结合起来。
- 好处:
1,防止命名冲突(js文件是互不干扰的)。2,代码复用(一个功能性的接口,可以供其他很多地方使用)3,高维护性
-
模块化规范产品:
-
es6之前的模块化规范有:
-
commonJS ----NodeJs,Broserify
-
AMD ----requireJs
-
CMD------seaJS
-
-
es6的模块化规范
- export命令用于规定模块的对外接口
- import命令用于输入其他模块提供的功能
-
-
模块化的使用:
-
//m1.js // 如果想向外面暴露数据,用export关键字 //1,分别暴露数据 export let school ='尚硅谷' export function teach(){ console.log('我们可以教给你开发技能'); } // 2,统一暴露数据 let school ='尚硅谷' function teach(){ console.log('我们可以教给你开发技能'); } export{school,teach} // 3,默认暴露 export default{ school:'****', teach:function(){ console.log('***') } } //module.html <!-- 相对于之前使用js文件的引入的方式是不同的 --> <script type="module"> // 引入m1.js 模块内容 // 这时候,m1.js里面暴露的变量就都存进了m1里面 import * as m1 from "m1.js"; //1.当js文件中是用分别暴露和统一暴露的时候,调用数据的方式是: m1.teach() //2,当js文件中是用默认方式进行暴露的时候,调用数据的方式是: m1.default.teach() </script>
-
使用script标签引入模块数据算法
-
// 1,通用方式 import * as m1 from "m1.js"; // 2,解构赋值的方式 import {school,teach} from "m1.js"; // 如果有重名的话,可以起一个别名 import {school as guigu,teach as teach1} from "m1.js" console.log(guigu,teach1); // 针对js中默认暴露的变量,引入的方式 // 这里的default必须要 起一个别名才可以,不能直接使用 import {default as m3} from "m1.js" // 3,简便形式,只针对默认暴露的模式 import m3 from "m1.js" console.log(m3)
-
-
浏览器使用ES6模块化的方式二
//modele.js // 如果在html里面的script标签里面引入模块 // 并且调用接口,会造成代码体积非常的大 // 所以可以把引入模块的代码全都放在一个js文件里 // 然后在html里面引入这个js文件即可 import * as m1 from "m1.js" import * as m2 from "m1.js" import * as m3 from "m1.js" // Module.html <script src="moduel.js" type="module"></script> <script> console.log(m1) console.log(m2) console.log(m3) </script>
-
在项目中使用这个ES6,模块化代码
- 由于浏览器的兼容性,并不是所有的浏览器都能使用上面的方式引入模块。
- 于是可使用Bable进行模块化代码的装换,将es6模块化代码装换成es5代码,然后再进行引用
- 步骤:
- 1,安装工具 bable-cli,bable-preset-env,browserify(webpack)
- 2, npx babel src/js -d dist/js --presets=bable -preset -env(就是将src/js下面的js文件装换到dist/js 目录里面)
- 3,打包 npx browserify ist/js/app.js -o dist/bundle.js
- 4,在讲打包好的js文件,引用在html里面,就可以正常的使用啦
-
es7新特性
1,Array.prototype.includes
includes方法用来检测数组中是否包含某个元素,返回布尔类型值。
和indexOf相比,是indexOf,如果 存在就返回数组下标,如果不存在就返回-1,而includes是返回的true/false
2,指数操作符
2的10次方,就写成 2** 10,和Math.pow(2,10)
es8新特性
async和await两种语法结合可以让异步代码像同步代码一样。
1,async
-
概念:
-
1,async函数的返回值为promise对象
-
2,promise对象的结果是由async函数执行的返回值决定的
-
// async 函数 async function timeout(ms) { // 这里的返回值可以是字符串,或者其他类型的值,但是最终这个函数返回的就是一个promise对象 // 如果返回值不是设置的promise对象,那这个函数的返回的promise对象的状态就是成功的,并且值就是设置的返回的值 // return 'zifuchuang' // 如果在函数体中,抛出一个错误,那么返回的promise对象就是一个失败的状态 throw new Error('出错啦') // 如果返回值是设置的promise对象,那这个函数返回的对象就是设置的返回的promise对象,其状态是一致的。 return new Promise(resolve => { setTimeout(resolve, ms); }) }
-
2,await表达式
- await必须写在async函数中
- await右侧的表达式一般为promise对象
- await返回的是promise成功的值
- await的promise失败了,就会抛出异常,需要 通过try…catch捕获处理
// 一个成功的promise对象 // 手动创建一个promise对象 const p1 = new Promise((resolve,reject)=>{ resolve('用户的数据') }) // await要放在async函数中 async function main(){ // await 返回的是右侧promise对象成功的值 let res=await p1; console.log(res) } main()//调用 main函数,返回的值就是'用户的数据' // 一个失败的promise对象 const p2 = new Promise((resolve,reject)=>{ reject('失败的数据') }) async function p2Err(){ try{ //如果得到的是一个失败的promise,那么就要用try..catch来进行捕获 let res=await p2Err; console.log(res); }catch(e){ console.log(e) } }
-
3,async和await相结合
案例1:
// async和await相结合进行异步任务
// 1,引入fs模块
const fs = require("fs")
// 读取第一个文件
function readfirst(){
fs.readFile('路径',(err,data)=>{
// 如果失败
if(err) reject(err)
// 如果成功
resolve(data)
})
}
function readsecond(){
fs.readFile('路径',(err,data)=>{
// 如果失败
if(err) reject(err)
// 如果成功
resolve(data)
})
}
function readtirth(){
fs.readFile('路径',(err,data)=>{
// 如果失败
if(err) reject(err)
// 如果成功
resolve(data)
})
}
// 声明一个 async函数
async function main(){
// 获取三个文件中的内容,得到的就是上面方法,读取到的数据
let first=await readfirst()
let second=await readsecond()
let tirth =await readtirth()
console.log(first+second+tirth);
}
案例2:
// 模拟ajax请求,返回的结果是promise对象
function sendAJAX(url){
// 为了得到一个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 方法测试
sendAJAX('https://api.apiopen.top/getJoke').then(value=>{
console.log(value)
},reson=>{})
// asncy和await结合测试
async function main(){
let res=await sendAJAX('https://api.apiopen.top/getJoke')
console.log(res)
}
4,对象扩展
- Object.values()方法返回一个给定对象的所有可枚举属性值的数组
- Object.entries()方法返回一个给定对象自身可遍历属性[ key,value] 的数组
<script>
const school={
name :"尚硅谷",
cityies:['北京','上海'],
xueke:['前端','java']
}
// 获取对象所有的键
console.log(Object.keys(school));
// 获取对象所有的值
console.log(Object.values(school));
// entries 返回对象自身可遍历的[key,value]值,这样是方便创建一个对象
const m = new Map(Object.entries(school))
// 返回对象属性的描述对象
console.log(Object.getOwnPropertyDescriptor(school))
</script>
es9新特性
1,扩展运算符和rest参数
rest参数与spread扩展运算符在es6中已经引入,不过在es6中只针对数组
在es9中为对象提供了像数组一样的rest参数和扩展运算符
<script>
// rest参数的使用
// 传来的前两个参数是存放在前两个参数里面,其他参数是存在user里面
function connect ({host,port,...user}){
console.log(host)
console.log(port)
console.log(user)
}
connect({
host :"localhost",
port : 3306,
username:'wlijun',
pwd:'root'
})
// spread运算符
// spread运算符对对象的操作
const skillone ={
a:'000',
b:'222'
}
const skilltwo ={
a:'111',
d:'222'
}
// 输出对象的属性和方法
// ...skillone //a:'000',b:'222'
// 使用运算符可以将两个对象放在同一个对象里面
// 如果有重名的,会进行值的覆盖
const skill={...skillone,...skilltwo}
console.log(skill)
</script>
2,正则扩展-命名捕捉数组
<script>
// 之前的做法
// 声明一个字符串
let str ='<a href="路径">尚硅谷</a>'
// 提取url 与标签文本
const reg=/<a href="(.*)">(.*)<\/a>/;
// 执行
// 返回的内容第一条是整个字符串,第二条是第一个(.*)的内容,第三条是第二个(.*)的内容
const res =reg.exec(str);
console.log(res[1])
console.log(res[2])
// 扩展的做法
let str ='<a href="路径">尚硅谷</a>'
const reg=/<a href="(?<url>.*)">(?<text>.*)<\/a>/
const res = reg.exec(str)
// 这里的res里面有groups属性,里面包含了url和text信息
console.log(res.groups.url)
console.log(res.groups.text)
</script>
3,正则扩展 dotAll模式
<script>
// dot . 元字符 除换行符以外的任意单个字符
// 需求,就是将下面的数据提取出来,保存成一个对象
let str=`
<ul>
<li>
<a>肖兴克的救赎</a>
<p>上映日期:</p>
</li>
<li>
<a>肖兴克的救赎</a>
<p>上映日期:</p>
</li>
</ul>
`
// 因为str中有换行符,所以在使用正则的时候,要把\s加上
const reg=/<li>\s+<a>(.*?)<\/a>\s+<\/li>/
// 在使用dot .的时候
const reg= /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs
// 想获得多个li中的标签的值,使用循环
let res;
let data=[]
while (res = reg.exec(str)){
console.log(res)
data.push({title:res[1],time:res[2]})
}
console.log(data)
</script>
es10
1,对象扩展,Object.fromEntries
<!-- 1,Object.fromEntries 用来创建一个对象,它的参数接收二维数组和map-->
<script>
// 参数是二维数组
const res=Object.fromEtries([
['name','尚硅谷'],
['xueke','java,大数据,前端,云计算']
]);
// 参数是Map
const m =new Map();
m.set('name','尚硅谷')
const res = Object.fromEtries(m);
// 和es8里面的Object.entries方法是逆运算,这个方法是把一个对象装换成二维数组
</script>
2,字符串扩展方法(trimStrat与trimEnd)
<!-- trim字符串扩展 -->
<script>
// trimStart清除字符串左侧的空白字符
// trimEnd清除字符串右侧的空白字符
const str=' i love you '
console.log(str.trimStart())
console.log(str.trimEnd())
</script>
3,数组扩展方法(flat和flatMap)
<script>
// falt 将一个多维数组转换为低维数组
const arr=[1,2,3,[5,6]]
console.log(arr.flat())//输出为[1,2,3,4,5,6]
// 参数为深度,是一个数字,默认是1
// 将一个三维数组转换成二维数组
const arr3=[1,2,[3,4,[5,6]]]
console.log(arr3.flat(2))// [1,2,[3,4,5,6]]
// flatMap
const arr=[1,2,3]
// 就是将数组里所有的返回结果都乘以10
const res =arr.Map(item=>item*10);
console.log(res) //[10,20,30]
// 当arr数组是一个二维数组的时候,上面的item里面返回的是一个数组,那输出的res就是一个二维数组
// 这时候就可以用flatMap,直接将返回结果降低为一维数组。
const res1=arr.flatMap(item =>[item*10])
</script>
4,symbol扩展Symbol.prototype.description
<!-- symbol扩展Symbol.prototype.description -->
<script>
// 用来获取字符串的描述
// 创建一个Symbol
let s =Symbol('尚硅谷')
console.log(s.description) //尚硅谷
</script>
es11新特性
1,私有属性
<script>
class Person{
// 共有属性
name;
// 私有属性
#age;
// 构造方法
constructor(name,age){
this.name=name;
this.#age = age;
}
intro(){
console.log(this.name)
console.log(this.age)
}
}
// 实例化
const girl = new Person('小红',18);
console.log(girl) //
console.log(girl.name) //可以输出
console.log(girl.#age) // 不可以输出,要在类内部进行使用
girl.intro() //直接调用类里面的方法,可以将age进行输出
</script>
2,promise.allSettled方法
<script>
// 这个方法接收promise数组,返回的是promise对象,
// 返回的结果一直是一个成功的状态,返回结果的值是promise对象数组,
// 数组里面是获取到promise对象的状态和对应的值
// 声明了两个promise对象
const p1 = new Promise((reslove,reject)=>{
setTimeout(()=>{
reslove('成功啦')
},1000)
})
const p2 = new Promise((reslove,reject)=>{
setTimeout(()=>{
reject('失败了')
},1000)
})
// 调用了 allsettied方法
const result =Promise.allSettled([p1,p2]);
console.log(result)
// all
const res = Promise.all([p1,p2]);
// 和上面不同的是,当p1和p2是成功的状态的时候,res才是一个成功的状态,否则就是失败的状态
// 而且返回值也是不相同的,就是当p1或p2是失败的状态,res就返回失败的peomise对象的值
console.log(res)
// 两者不同是,如果需要两个promise都成功的话,才能往下进行,那就使用all
// 如果是不管成功失败都能往下进行,或者可以得到两者的值,那就用allSettled方法
</script>
3,matchAll方法
<!-- matchAll方法 可批量提取数据-->
<script>
let str=`
<ul>
<li>
<a>肖兴克的救赎</a>
<p>上映日期:</p>
</li>
<li>
<a>肖兴克的救赎</a>
<p>上映日期:</p>
</li>
</ul>
`
// 声明正则
const reg = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/sg
// 调用方法,
const res = str.matchAll(reg);//返回的结果是一个可迭代对象,有一个next方法,可用for of循环
for(let v of res){
console.log(v)
}
const arr = [...res] //转换成数组,就可以得到全部的结果啦
</script>
4,可选链操作符
<!-- 可选链操作符 -->
<script>
// 形式?.
function main(config){
// 当我们要获取db里面的username的时候
// 要进行判断config是否传进来,config里面是否有db属性等
const dbHost =config && config.db && config.db.host
// 当时使用可选链操作符的时候,可以直接写,如果没有传进来参数,也不会报错,会返回undefined
const dbhist=config?.Person?.host
}
main({
db:{
host :'***',
username:'***',
}
})
</script>
5,动态import
<!-- 动态import -->
<script>
// 对js模块进行按需加载
// 当点击这个按钮的时候,才导入某一个模块,引用里面的方法
// 获取元素
const btn = document.getElementById('btn');
// ***.js
export function hello (){
console.log('hello')
}
btn.onclick = fucntion(){
// import返回的对象是一个promise对象,这个对象返回的就是js模块中暴露出来的方法
import('**.js').then(module =>{
module.hello();
})
}
</script>
6,BigInt 大整数
<!-- BigInt大整形 -->
<script>
//数字后面加上n的意思就是表示这个数是一个大整形
let n = 521n;
// 大整形函数
let n1= 123
console.log(BigInt(n1)) //123n
// 大数值运算
// 对于number来说,到达一个最大值,再相加的时候,就会出问题
// 所以对于很大的数值,就可以用大整形
let max =Number.MAX_SAFE_INTEGER;
// 这里的大整形是不能和整型相加的,都需要装换成大整形
console.log(BigInt(max)+BigInt(1))
</script>
7,绝对全局对象