文章目录
传送门:阮一峰ES6入门教程
可用API https://api.apiopen.top/api/sentences
let,const
let
let声明变量的语法:
//直接声明
let a;
//声明并赋值
let b = 100;
//批量声明并赋值
let c = 'iloveyou',h = [];
- let 不能重复声明
- 块级作用域
- 不存在变量提升(var执行前先收集,再声明前赋值为undefined)
- 不影响作用域链
{
let school = "东北师大";
function fn(){
console.log(school);//函数作用域内没有,会向上一级寻找
}
fn();
}
let作用域案例:大div套三个小div,依次点击使小div变成粉色
第一次使用var遍历,不成功,因为var是全局作用域,遍历之后i = 3,点击时没有items[3]
var items = document.getElementsByClassName('item');
// 遍历并绑定事件
for(var i = 0;i < items.length;i ++){
items[i].onclick = function(){
// this.style.background = 'pink';
items[i].style.background = 'pink';
}
}
console.log(window.i);
// 全局变量i循环体,每一次让i自增
for循环分解:(var)
{
var i = 0;
}
{
var i = 1;
}
{
var i = 2;
}
//i全局存在,执行onclick时作用域找不到,向外层寻找,此时i = 3
for循环分解:(let)
{
let i = 0;
items[i].onclick = function(){
items[i].style.background = 'pink';
//此函数块内没有i,顺着作用域链往上i = 2
}
}
{
let i = 1;
items[i].onclick = function(){
items[i].style.background = 'pink';
//顺着作用域链往上 i = 1.
}
}
{
let i = 2;
items[i].onclick = function(){
items[i].style.background = 'pink';
}
}
const
const声明常量,常量是值不能修改的量
const SCHOOL = '东北师大';
console.log(SCHOOL);
注意:
- 一定要赋初始值
- 一般常量名使用大写
- 常量值不能修改
- 也是块级作用域
- 对于数组和对象的属性做修改,不算作对常量进行修改,不报错(因为常量指向的地址没有改变)
const TEAM = ['UZI','MXLG','MING','LETME']; TEAM.push('meiko');
因此数组和对象使用const声明,避免误操作修改数据的值
箭头函数
let add = function(a,b){
return a + b;
}
//省略function,加 =>
//小括号写参数
let fn = (a,b) => {
return a + b;
}
let result = fn(1,2);
箭头函数特性:
- this是静态的,this始终指向
函数声明时所在作用域下
的this的值 - 不能作为构造函数实例化对象
- 不能使用arguments变量(注:arguments变量用来保存实参)
第一点解释
直接调用
function getName(){
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
}
window.name = '东北师大';
const school = {
name:"NorthEast"
}
//普通函数直接调用this值指向window
getName();//东北师大
//箭头函数在全局作用域下调用,this值也是指向window
getName2();//东北师大
使用call方法调用(call方法会改变函数内部this的值)
function getName(){
console.log(this.name);
}
let getName2 = () => {
console.log(this.name);
}
window.name = '东北师大';
const school = {
name:"NorthEast"
}
getName.call(school);//NorthEast
//箭头函数this值是静态的,指向函数在声明时所在作用域下那个this值
getName2.call(school);//东北师大
** 第二点解释**
let Person = (name,age) => {
this.name = name;
this.age = age;
}
let me = new Person('xiao',30);
console.log(me);//报错
第三点解释
// let add = (n) => {
// return n + n;
// }
let add = n => {
return n + n;
}
console.log(add(9));
箭头函数的简写
- 省略小括号(当形参有且只有一个时)
// let pow = (n) => {
// return n * n;
// }
let pow = (n) => n * n;
console.log(pow(9));
- 省略花括号(当代码体只有一条语句时)
此时return也必须省略,语句执行结果就是函数返回值
<script>
// let pow = (n) => {
// return n * n;
// }
let pow = (n) => n * n;
console.log(pow(9));
箭头函数案例
1.点击div,2s后变成粉色
div{
width: 80px;
height: 60px;
background-color: rgb(104, 174, 240);
}
<div id="ad"></div>
let ad = document.getElementById('ad');
ad.addEventListener("click",function(){
//把外层函数作用域下的this进行保存
let _this = this;
setTimeout(function(){
//这里的this指向window,window没有style属性
//当前作用域找不到_this,往外层找
_this.style.background = 'pink';
},2000)
});
let ad = document.getElementById('ad');
ad.addEventListener("click",function(){
setTimeout(() => {
//箭头函数,this指向声明时所在函数的this,即addEventListener函数
//该函数this指事件源ad
this.style.background = 'pink';
},2000)
});
2.从数组中返回偶数的元素
const arr = [1,6,9,10,100,25];
//filter返回一个新数组,新数组中是满足条件的元素
//item是必选参数,表示当前元素的值
// 第一种
// const result = arr.filter(function(item){
// if(item % 2 === 0) return true;
// else return false;
// })
// 第二种
// const result = arr.filter((item) => {
// if(item % 2 === 0) return true;
// else return false;
// })
// 第三种
const result = arr.filter(item => item % 2 === 0);
console.log(result);
箭头函数
- 适合用于与this无关的回调:定时器,数组的方法回调
- 不适合与this有关的回调:DOM元素的事件回调(此时this应该指向事件源),对象的方法
{
name:'东北师大',
getName:function(){
//this指向所在这个函数块
this.name;
}
}
{
name:'东北师大',
getName:() => {
//this指向外层作用域的this值
this.name;
}
}
变量的解构赋值
解构赋值:从数组和对象中提取值,赋值给变量
数组解构
const F4 = ['小沈阳','刘能','赵四','宋小宝'];
//相当于声明了四个变量,
let [xiao,liu,zhao,song] = F4;
console.log(xiao);
console.log(liu);
console.log(zhao);
console.log(song);
对象解构
const zhao = {
name:"赵本山",
age:"不详",
xiaopin:function(){
console.log("我可以演小品");
}
}
//变量名要一样,zhao里没有解构器
let {name,age,xiaopin} = zhao;
console.log(name);
console.log(age);
console.log(xiaopin);
xiaopin()
const zhao = {
name:"赵本山",
age:"不详",
xiaopin:function(){
console.log("我可以演小品");
}
}
//单独解构
let {xiaopin} = zhao;
xiaopin();
//避免写zhao.xiapin();
模板字符串(反引号)
let string = `我也是一个字符串哦!`;
console.log(string,typeof string);
用单引号
let string = '<ul>'
+'<li>沈腾</li>'
+'<li>玛丽</li>'
+'<li>魏翔</li>'
+'<li>艾伦</li>'
+'</ul>'
console.log(string);
用反引号
let string =
`<ul>
<li>沈腾</li>
<li>玛丽</li>
<li>魏翔</li>
<li>艾伦</li>
</ul>`
console.log(string);
特性:
- 内容中可以出现换行符,单引号双引号不可以
- 变量拼接
let lovest = '魏翔';
let out = `${lovest}是我心目中最搞笑的演员`;
console.log(out);
对象的简化写法
let name = '东北师大';
let gradu = function(){
console.log("我们毕业啦");
}
const school = {
//简化书写内容
name,
gradu,
beauty(){
console.log("真好看");
}
// beauty:function(){
// console.log("我们学校真好看");
// }
//省略:function
}
console.log(school);
函数参数的默认值设置
ES6允许给函数参数赋初始值,位置要靠后
function add(a,b,c = 10){
return a + b + c;
}
let result = add(1,2);
console.log(result);
与解构赋值结合
// function connect(options){
// let host = options.host;
// let username = options.username;
// }
// 与解构赋值结合
function connect({host,username,password,port}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
//调用方法,传入一个对象
connect({
host:'localhost',
username:'root',
password:'root',
port:3306
})
给属性赋初始值
function connect({host = "127.0.0.1",username,password,port}){
console.log(host);
console.log(username);
console.log(password);
console.log(port);
}
//调用方法,传入一个对象
connect({
//host:'localhost',
username:'root',
password:'root',
port:3306
})
rest参数
代替arguments,用于获取函数的实参
//ES5获取实参的方式
function data(){
//arguments原型是对象
console.log(arguments);
}
data('哈哈','嘻嘻','呵呵');
// rest参数
//声明方式:...标识符
function data(...args){
//数组,可以使用filter,some,every,map方法
console.log(args);
}
data('阿娇','白芷','思慧');
//rest参数必须放在最后
function fn(a,b,...args){
console.log(a);
console.log(b);
console.log(args);
}
fn(1,2,3,4,5);
拓展运算符
...
将数组 转换为 逗号分隔的参数序列
const cities = ['新疆','西藏','青海'];
function chunwan(){
console.log(arguments);
}
chunwan(cities);//一个成员
chunwan(...cities);//三个成员
// 等同于chunwan('新疆','西藏','青海');
拓展运算符的应用
1.数组的合并
const kuaizi = ['王太利','肖央'];
const fenghuang = ['曾毅','玲花'];
// const zuixuanxiaopingguo = kuaizi.concat(fenghuang);
//其实是...把数组转化为逗号分隔的参数序列
const zuixuanxiaopingguo = [...kuaizi,...fenghuang];
console.log(zuixuanxiaopingguo)
2.数组的克隆(引用类型数据,浅拷贝)
const sanzhihua = ['E','G','M'];
const sanyecao = [...sanzhihua];
console.log(sanyecao);
3.将伪数组转为真正的数组
const divs = document.querySelectorAll('div');
console.log(divs);//对象
const divArr = [...divs];
console.log(divArr);//数组
symbol
symbol是es6的新的原始数据类型,表示独一无二的值。是js的第七种数据类型
Symbol创建的两个方式
//Symbol是一个函数
let s = Symbol();
let s2 = Symbol('尚硅谷');
let s3 = Symbol('尚硅谷');
console.log(s2 === s3);//false
//Symbol.for创建,可以通过描述字符串得到唯一的symbol值
//Symbol是一个对象
let s4 = Symbol.for('尚硅谷');
let s5 = Symbol.for('尚硅谷');
console.log(s4 === s5);//true
- Symbol的值是惟一的,用来解决命名冲突的问题
- Symbol的值不能与其他数据进行运算
- Symbol定义的对象属性不能用for…in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
js数据类型:USONB
u:undefined
s:string symbol
o:object
n:null number
b:boolean
Symbol的使用场景
给对象添加Symbol类型的属性和方法,表示独一无二的值,防止与原有属性名产生冲突
1.
//快速安全地把方法(和up,down相关)添加到对象game上
let game = {
name:'俄罗斯方块',
};
//声明一个对象,两个属性对应的都是Symbol类型的值
let methods = {
up:Symbol(),
down:Symbol()
};
// 给game拓展方法
game[methods.up] = function(){
consolelog("我可以改变形状");
}
game[methods.down] = function(){
console.log("我可以快速下降");
}
console.log(game);
let youxi = {
name:"狼人杀",
//不可以直接写Symbol,因为Symbol是一个动态值,不是固定属性
// Symbol():function(){}
//Symbol是动态表达式,应该添上中括号
[Symbol('say')]:function(){
console.log("我可以发言")
},
[Symbol('zibao')]:function(){
console.log("我可以自爆");
}
}
console.log(youxi)
Symbol的内置值
除了定义自己使用的Symbol唯一值以外,ES6还提供了11个内置的Symbol属性,Symbol.xxx
整体作为对象的属性而存在
class Person{
static [Symbol.hasInstance](param){
console.log(param);
console.log("我被用来检测类型了");
}
}
let o = {};
console.log(o instanceof Person)
const arr = [1,2,3];
const arr2 = [4,5,6];
arr2[Symbol.isConcatSpreadable] = false;
console.log(arr.concat(arr2));
迭代器
迭代器(iterator)是一种接口。ES6创造一种新的遍历命令for...of
,iterator接口主要供该命令消费
原生具备iterator的数据(可用for…of遍历)
- Array
- Arguments
- Set
- Map
- String
- TypedArray
- NodeList
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用for...of遍历
//for...in保存键名,for...of保存键值
for(let v of xiyou){
console.log(v);
}
console.log(xiyou);
遍历的工作原理:
Symbol.iterator()
创建一个指针对象,指向当前数据结构的起始位置- 返回的指针对象有next(),第一次调用next(),指针自动指向数据结构第一个元素
- 接下来不断调用,指针往后移,直到指向最后一个元素
- 每调用next()返回一个包含value和done属性的对象
const xiyou = ['唐僧','孙悟空','猪八戒','沙僧'];
//使用for...of遍历
//for...in保存键名,for...of保存键值
for(let v of xiyou){
console.log(v);
}
console.log(xiyou);
let iterator = xiyou[Symbol.iterator]();
//调用next()
console.log(iterator.next());//指针指向第一个成员唐僧
console.log(iterator.next());//孙悟空
console.log(iterator.next());//猪八戒
console.log(iterator.next());//沙僧
console.log(iterator.next());//value:undefined,done:true
迭代器案例
自定义(按自己意愿)遍历数据时,想到迭代器
const banji = {
name:'终极一班',
stus:[
'xiaoming',
'xiaoning',
'xiaotian',
'knight'
],
[Symbol.iterator](){
//索引变量
let index = 0;
// let _this = this;
//返回一个对象
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...of遍历对象,返回数组的成员.对对象进行遍历
for(let v of banji){
console.log(v);
}
//banji.stus.forEach()不符合面向对象的思想
生成器
生成器是异步编程的一种解决方案
三个特殊性:
//声明要加*
function * gen(){
console.log("hallo generator");
}
//调用函数并执行next方法才能执行
let iterator = gen();
iterator.next();
function * gen(){
//生成器函数可以出现yield语句,yield可算作函数代码的分隔符
// 代码块一
console.log(111);
yield '一直没有耳朵';
// 代码块二
console.log(222);
yield '一直没有尾巴';
//三
console.log(333);
yield '真奇怪';
// 四
console.log(444);
}
//调用函数并执行next方法才能执行
let iterator = gen();
iterator.next();
iterator.next();
iterator.next();
iterator.next();
function * gen(){
//生成器函数可以出现yield语句,yield可算作函数代码的分隔符。next函数遇到yield会停止
yield '一直没有耳朵';
yield '一直没有尾巴';
yield '真奇怪';
}
let iterator = gen();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//遍历
for(let v of gen()){
console.log(v);
}
//整体的函数传参
function * gen(arg){
console.log(arg);
let one = yield 111;//第一个yield
yield 222;
yield 333;
}
// 执行获取迭代器对象
let iterator = gen('AAA');
console.log(iterator.next());
//next语句也可以传参,作为上一个yield的返回结果
console.log(iterator.next('BBB'));//第二个next语句
生成器案例
案例一
1s后控制台输出111,2s后输出222,3s后输出333
方案一:回调地狱
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
},3000)
},2000)
},1000)
方法二:生成器
//声明三个函数分别完成三个异步任务
function one(){
setTimeout(() => {
console.log(111);
iterator.next();
},1000)
}
function two(){
setTimeout(() => {
console.log(222);
iterator.next();
},2000)
}
function three(){
setTimeout(() => {
console.log(333);
iterator.next();
},3000)
}
function * gen(){
yield one();
yield two();
yield three();
}
let iterator = gen();
iterator.next();
案例二
模拟获取用户数据,订单数据,商品数据(按顺序)
function getUsers(){
setTimeout(() => {
let data = '用户数据';
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();//执行第一段代码
//无法保持数据关联度
// getUser();
// getOrderss();
// getGoods();
promise
promise是ES6引入的异步编程的新解决方案。语法上promise是一个构造函数,可以实例化对象,用来封装异步操作。可以获取其成功或者失败的结果
// 实例化promise对象,
// 接收函数类型的参数,该函数有resolve,reject两个形参
const p = new Promise(function(resolve,reject){
//封装异步操作
setTimeout(function(){
let data = '数据库中的用户数据';
//调用resolve和reject函数改变promise对象的状态
//对象的状态变成成功
// resolve(data);
let err = '数据读取失败';
reject(err);
},1000)
});
//成功则调用promise对象的then方法
// then方法接收两个函数型参数,每个函数一个形参
p.then(function(value){
//成功则调用第一个回调函数的方法
console.log(value);
},function(reason){
//失败执行第二个回调函数
console.error(reason);
})
promise封装读取文件内容
读取文件是异步操作。避免代码持续向内部缩进
const fs = require('fs');
/* 调用readFile方法读取文件
fs.readFile('./resources/为学.md',(err,data) => {
if(err) throw err;
console.log(data.toString());
}) */
// 使用promise封装
const p = new Promise(function(resolve,reject){
fs.readFile("./resources/为学.md",(err,data) => {
if(err) reject(err);
resolve(data);
})
});
p.then(function(value){
console.log(value.toString());
},function(reason){
console.log("读取失败");
});
Promise封装Ajax请求
const p = new Promise((resolve,reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest();
// 2.初始化
xhr.open("GET","https://api.apiopen.top/api/sentences");
// 3.发送
xhr.send();
// 4.绑定事件
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断响应状态码
if(xhr.status >= 200 && xhr.status < 300){
//console.log(xhr.response);
resolve(xhr.response);
}else{
//console.log(xhr.status);
reject(xhr.status);
}
}
}
})
//指定成功和失败的回调
//在.then方法回调,结构更清晰
p.then(function(value){
console.log(value);
},function(reason){
console.error(reason);
})
.then方法
const p = new Promise((resolve,reject) => {
setTimeout(function(){
resolve("用户数据");
// reject("出错了");
},1000)
})
//.then方法返回状态也是一个promise,返回结果由回调函数的执行结果确定
//1.不写返回值返回undefined
const result = p.then(value => {
console.log(value);
//2.如果返回值是非Promise属性,状态为成功,返回值是对象的成功的值
// return 'iloveyou'
//3.如果返回promise对象,内部promise状态决定.then方法promise的状态
// return new Promise((resolve,reject) => {
// // resolve('ok');
// reject('error');
// });
// 4.抛出错误
// throw '出错啦!';//抛出字符串
throw new Error("出错啦!");
},reason => {
console.warn(reason);
})
console.log(result);
链式调用(杜绝回调地狱)
p.then(value => {},reson => {})
.then(value => {},reson => {})
实践练习:多个文件内容读取
const fs = require("fs")
//使用回调地狱实现
/* fs.readFile('./resources/为学.md',(err,data1) => {
fs.readFile('./resources/逍遥游.md',(err,data2) => {
fs.readFile('./resources/李白.md',(err,data3) => {
//回调地狱:重名不容易发现,调试不方便
let result = data1 + '\r\n' + data2 + '\r\n' + data3;
console.log(result);
})
})
}) */
//使用promise实现
const p = new Promise((resolve,reject) => {
fs.readFile('./resources/为学.md',(err,data) => {
resolve(data);
});
});
p.then((value) => {
//
return new Promise((resolve,reject) => {
fs.readFile('./resources/逍遥游.md',(err,data) => {
resolve([value,data]);//resolve后,then方法返回的promise也成功
});
});
}).then(value => {//此时value是第1、2文件的数组
return new Promise((resolve,reject) => {
fs.readFile('./resources/李白.md',(err,data) => {
// 数组压入
value.push(data);
resolve(value);//value是三个文件的数组
})
})
}).then(value => {
// console.log(value);
console.log(value.join('\r\n'));
})
.catch方法
语法糖,then方法不指定第一个参数和catch是一样的
const p = new Promise((resolve,reject) => {
setTimeout(() => {
// 修改p状态为失败,并设置失败的值
reject("出错啦");
},1000)
});
// p.then(function(value){},function(reason){
// console.error(reason);
// })
p.catch(function(reason){
console.warn(reason);
})
集合
ES6提供了新的数据结构Set(集合),类似于数组,但成员的值都是惟一的
实现了iterator接口,可以使用let of遍历
let s = new Set();
//去重的方法之一
let s2 = new Set(['大事','小事','好事','坏事','小事']);
//元素个数
console.log(s2.size);
s2.add('喜事');
s2.delete('坏事');
s2.has('好事');
// s2.clear();
for(let v of s2){
console.log(v);
}
求集合的去重、交并差案例
let arr = [1,2,3,4,5,4,3,2,1];
// 1.数组去重
let result1 = new Set(arr);//集合,不是数组
result1 = [...new Set(arr)];//扩展运算符
//2.交集
let arr2 = [4,5,6,5,6];
let result2 = [...new Set(arr)].filter(item => {
//result2是数组,filter返回一个新数组。item是必选参数
let s2 = new Set(arr2);
// s2是集合,有has方法判断
if(s2.has(item)) return true;
else return false;
})
// 简易写法
// let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item));
// 3.并集
// ...将数组、集合分成逗号分开的参数序列
//整体加上中括号后又是一个数组
let union = [...new Set([...arr,...arr2])];
//4.差集
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)));
console.log(diff);
map
ES6提供了Map数据结构。类似于对象,升级版的对象,是键值对的集合,但是“键”不限于字符串,可以是各种类型的值。
Map也实现了iterator接口,可以用扩展运算符...
和for...of...
遍历
Map的属性和方法:
- size 返回Map元素个数
- set 添加新元素,返回当前Map
m.set(‘键名’,'键值')
- get 返回键名对象的键值
- has
- clear 返回undefined
let m = new Map();
m.set('name','尚硅谷');
m.set('change',function(){
console.log("我们可以改变你");
})
let key = {
school:'ATGUIGU'
};
m.set(key,['北京','上海','深圳']);
console.log(m.size);//长度
console.log(m.get('change'));//获取
console.log(m.get(key));//获取
m.delete('name');//删除
// m.clear();//清空
//遍历
for(let v of m){
console.log(VBArray);
}
class
ES6提供了更接近传统语言(java,c++)的写法,引入类的概念,可以当做语法糖,其功能ES5基本都能完成,新的写法可以让对象原型写得更清晰,更面向对象。
ES5和ES6中class的语法区别
// ES5写法
//手机
function Phone(brand,price){
//this是所在函数所属对象的引用。
//哪个对象调用了this所在的函数,this就代表哪个对象
this.brand = brand;
this.price = price;
}
//添加方法
//在一个对象的上下文中应用另一个对象的方法
Phone.prototype.call = function(){
console.log("我可以打电话!");
}
//实例化对象
let Huawei = new Phone('华为','5999');
Huawei.call();
console.log(Huawei);
//ES6
class Person{
// 构造方法是特殊方法,名字不可修改,自动执行
//使用new+类名时会自动执行constructor方法
constructor(name,school){
this.name = name;
this.school = school;
}
//加方法,必须使用该语法
call(){
console.log("我可以干活!");
}
}
//类就绪,实例化
let Huang = new Person("YUYING","NORTH");
console.log(Huang);
Huang.call();
static静态成员
// ES5
// 构造函数本身也是对象
function Phone(){
//
}
// 这两个属性属于函数对象,不属于实例对象
//这样的属性是静态成员
Phone.name = '手机';
Phone.chage = function(){
console.log("我可以改变世界");
}
//实例对象没有构造函数对象的属性
// 实例对象属性和构造函数原型对象相同
Phone.prototype.size = '5.5inch';
let nokia = new Phone;
console.log(nokia.size);
// 实例对象和函数对象属性不同
//ES6
class Person{
// static标注的属性和方法,属于类,不属于实例对象
static name = '人';
static change(){
console.log("读书改变命运");
}
}
let Huang = new Person;
console.log(nokia.name);
console.log(Person.name);
继承
ES5构造函数继承
// ES5构造函数继承
//父级构造函数
function Phone(brand,price){
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function(){
console.log("我可以打电话!");
}
//子级构造函数
function smartPhone(brand,price,color,size){
//先调用父级构造函数初始化代码
// 通过call方法修改this值,this指向smartPhone的实例对象
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.game = function(){
console.log("可以打游戏");
}
// 实例化
const Apple = new smartPhone('苹果',5000,'red','5.5inch');
console.log(Apple);
class类继承
class Phone{
constructor(brand,price){
this.brand = brand;
this.price = price;
}
call(){
console.log('我可以打电话');
}
}
class smartPhone extends Phone {
//构造方法
constructor(brand,price,color,size){
//调用父类的构造方法进行初始化,super就是父类的constructor方法
super(brand,price);//Phone.call(this,brand,price);
this.color = color;
this.size = size;
}
//添加子类独有的方法
photo(){
console.log("拍照");
}
game(){
console.log("游戏");
}
}
//实例化
const OPPO = new smartPhone('欧珀',799,'red','5.5inch');
console.log(OPPO);
OPPO.call();
OPPO.game();
OPPO.photo();
子类对父类的方法重写
子类不可以直接调用父类的同名方法
getter和setter
对对象的属性进行方法绑定
get:封装对象动态属性
set:添加判断
class Phone{
//没有构造方法也是合理的
get price(){
console.log("价格属性被读取了");
return 'iloveyou'
}
set price(newVal){
console.log('价格属性被修改了');
}
}
//实例化对象
let s = new Phone();
//读取price属性,就执行里边代码
//返回值就是属性的值
console.log(s.price);
s.price = 'free';
模块化
模块化:将大文件拆分成小文件,再将小文件组合起来
好处:
- 防止面临冲突
- 代码复用
- 高维护性
export
- 分别暴露
export let school = "NENU"
export function study(){
console.log("I LOVE STUDY")
}
- 统一暴露
let school = "NENU"
function study(){
console.log("I LOVE STUDY")
}
export{
school,study
}
- 默认暴露
//暴露数据可以是任意类型,可以数数字,字符串等,对象居多
export default{
school:"NENU",
study:function(){
console.log("I LOVE STUDY")
}
}
//该方法调用时要多加一层default结构
//message.default.study();
import
- 通用导入方式
<script type = "module">
import * as message from "./index.js"
console.log(message);
</script>
- 解构赋值形式
<script type = "module">
import {school,study} from "./index.js";
//使用别名避免冲突
import {school as NE,qualify} from './index2.js';
// m3是对象,有一个default属性,里边存的是变量
//必须给default取别名
import {default as m3} from "./index.js";
console.log(m3)
console.log(school);
console.log(study);
console.log(NE);
console.log(qualify);
</script>
3.简便形式(针对默认暴露)
<script type = "module">
import message from "./index.js"
console.log(message)
</script>
其它引入方式:在script标签中加入src和type属性,另建一个js文件
模块化代码在项目中的引用(babel对ES6模块化代码转换)
环境配置
npm init --yes
npm i babel-cli babel-preset-env browserity -D
async和await(ES8)
async
异步编程的新解决方案
async函数返回promise对象
promise对象的结果由async函数执行的返回值决定
async function fn(){
//只要return的不是promise对象,结果都是promise对象,状态是fulfilled
// return 'hhh';
//若抛出错误,返回rejected的promise
// throw new Error("出错啦");
//返回promise对象,由promise状态决定
return new Promise((resolve,reject) => {
// resolve('成功的数据');
reject('失败的数据');
})
}
const result = fn();
console.log(result);
result.then(value => {
console.log(value)
},reason => {
console.warn(reason)
})
await
await必须卸载async函数里,右侧为promise对象,返回promise成功的值,如果promise失败,抛出异常,通过try…catch处理
const p = new Promise((resolve,reject) => {
// resolve("成功数据");
reject("失败数据");
})
async function main(){
try{
let result = await p;//await返回结果就是promise对象成功的数据
console.log(result);
}
catch(e){//catch语句可以得到失败的结果
console.log(e);
}
}
main();
async和await结合读取文件内容
//引入文件模块
const fs = require('fs')
// 使用promise函数调用
function readLibai(){
return new Promise((resolve,reject) => {
fs.readFile('./resources/李白.md',(err,data) => {
if(err) reject(err);
resolve(data);
})
})
}
function readWeixue(){
return new Promise((resolve,reject) => {
fs.readFile('./resources/为学.md',(err,data) => {
if(err) reject(err);
resolve(data);
})
})
}
// async,await函数接收
async function main(){
let libai = await readLibai();
let weixue = await readWeixue();
console.log(libai.toString())
console.log(weixue.toString())
}
// 调用
main()
async和await结合发送ajax请求
function sendAjax(url){
return new Promise((reject,resolve) => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', url);
// 发送
xhr.send();
// 事件绑定
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status <= 300){
resolve(xhr.response);
}
else{
reject(xhr.status);
}
}
}
})
}
// promise then 测试
const result = sendAjax("https://api.apiopen.top/api/sentences").then((value => {
console.log(value);
},reason => {}))
数值扩展
//js最小精度
console.log(0.1+0.2);//0.30000000000000004
console.log(0.1 + 0.2 === 0.3); //false
function equal(a,b){
if(Math.abs(a - b) < Number.EPSILON){
return true;
}
else{
return false;
}
}
console.log(equal(0.1 + 0.2,0.3))//true
//进制
let b = 0b1010;//二进制
let o = 0o777;//八进制
let x = 0xff;//十进制
//判断一个数值是否是有限数
console.log(Number.isFinite(100));//true
console.log(Number.isFinite(100/0));//false
console.log(Number.isFinite(Infinity));//false
//检测一个数字是否是NaN
console.log(Number.isNaN(123));//false
//ES6把isNaN作为Number的一个方法
// 把字符串转化为整数/浮点数
console.log(Number.parseInt('5213'));//5213
console.log(Number.parseFloat('3.1415神奇'))//3.1415
//判断一个数字是否是证书
console.log(Number.isInteger(5));//true
console.log(Number.isInteger(2.5));//false
//将数字的小数部分抹掉
console.log(Math.trunc(3.5));//3
//检测一个数是正数、负数还是零
console.log(Math.sign(100));//1
console.log(Math.sign(0));//0
console.log(Math.sign(-100));//-1
对象方法拓展
// Object.is判断两个值是否完全相等
console.log(Object.is(120,121));//false
console.log(Object.is(NaN,NaN));//true
console.log(NaN === NaN);//false
//Object.assign 对象合并,适合配置合并
//重名的覆盖掉,不同名的都显示
const config1 = {
host:'localhost',
port:3306,
name:'root',
address:'xxxx'
}
const config2 = {
host:'http://atguigu.com',
port:33060,
name:'atguigu.com',
tel:1804622222
}
console.log(Object.assign(config1,config2));
//Object.setPrototypeOf 设置原型对象
//Object.getPrototypeof
const school = {
name:'xiaoming'
}
const cities = {
province:['guangxi','jilin','beijing']
}
Object.setPrototypeOf(school,cities);
console.log(school);
console.log(Object.getPrototypeOf(school));
//但是不建议这么添加属性,定义时直接写上较佳
对象展开(rest参数和扩展运算符)
rest参数和spread扩展运算符在ES6中已经引入,只针对数组
ES9中为对象也提供了rest参数和扩展运算符
<script>
//定义方法
function connect({host,port,...user}){
console.log(host);
console.log(port);
console.log(user);
}
// 调用方法
connect({
host:'127.0.0.1',
port:3306,
//后面两个参数会存到user变量中
username:'root',
password:'port'
});
</script>
扩展运算符:将对象展开,形成参数序列
<script>
const skillOne = {
q:'天音波'
}
const skillTwo = {
w:'金钟罩'
}
const skillThree = {
e:'天音波'
}
const skillFour = {
r:'猛龙摆尾'
}
// 把四个对象的属性放到一个对象里
const mangseng = {
...skillOne,
...skillTwo,
...skillThree,
...skillFour
}
console.log(mangseng);//{ "q": "天音波","w": "金钟罩", "e": "天音波","r": "猛龙摆尾"}
</script>
正则拓展
命名捕获分组
对分组匹配结果命名,方便对结果的处理
- 原始做法(groups:undefined)
let str = '<a href="http://www.baidu.com">百度</a>'
// 提取url和标签文本
const reg = /<a href="(.*)">(.*)<\/a>/
const result = reg.exec(str)
//数组,result[0]是整个整个正则匹配,result[1]是第二个括号,result[2]是第二个括号
console.log(result[1])
console.log(result[2])
- 正则捕获分组做法(groups:键值对)
避免更改需要获取的属性时引起的数组下标变化,因此更易于代码维护
let str = '<a href="http://www.baidu.com">百度</a>'
const reg = /<a href="(?<url>.*)">(?<text>.*)<\/a>/
const result = reg.exec(str);
console.log(result)
console.log(result.groups.url)
console.log(result.groups.text)
反向断言
断言:判断匹配结果是否正确。
根据目标值的前或后,做唯一性识别
// 正向断言
let str = 'NENU20230329图书馆1042啦啦啦'
const reg = /\d+(?=啦)/
const result = reg.exec(str)
console.log(result)
console.log(result[0])
//反向断言
const reg2 = /(?<=馆)\d+/
const result2 = reg2.exec(str)
console.log(result2)
dotAll模式
加了s之后.可以匹配任意字符
<script>
let str = `
<ul>
<li>
<a>肖申克的救赎</a>
<p>上映日期:1994-09-10</p>
</li>
<li>
<a>阿甘正传</a>
<p>上映日期:1994-07-06</p>
</li>
</ul>`;
// \s表示单个空白字符,加?表示禁止贪婪
const reg = /<li>\s+<a>(.*?)<\/a>\s+<p>(.*?)<\/p>/
const result = reg.exec(str);
console.log(result);
//使用dotAll模式
// g表示全局匹配
const reg2 = /<li>.*?<a>(.*?)<\/a>.*?<p>(.*?)<\/p>/gs
let data = [];
let result2;
while(result2 = reg2.exec(str)){
data.push({title:result2[1],time:result2[2]});
}
console.log(data);
</script>
对象方法拓展
Object.fromEntries 将二维数组或者map转化为对象
Object.Entries 将对象转化为二维数组
// 二维数组
const result = Object.fromEntries([
['name','NENU'],
['subject','math,art']
])
// Map
const m = new Map();
m.set('name','TONG');
const result2 = Object.fromEntries(m);
console.log(result)
/* {"name": "NENU","subject": "math,art"} */
console.log(result2)
/* {"name": "TONG"} */
// Object.entries将对象转化为二维数组
const arr = Object.entries({
name:"HYY"
})
console.log(arr)
字符串方法拓展
trim清除字符串两端的空白字符
let str = ' iloveyou '
console.log(str)
console.log(str.trimStart())
console.log(str.trimEnd())
数组方法拓展
flat将多维数组转化为低维数组,参数为深度
const arr = [1,2,3,4,[5,6]];
console.log(arr.flat());//[1,2,3,4,5,6]
const arr2 = [1,2,3,[4,5,6,[7,8,9]]];
console.log(arr2.flat())//[1,2,3,4,5,6,[7,8,9]]
console.log(arr2.flat(2))//[1,2,3,4,5,6,7,8,9]
const arr3 = [1,2,3]
// 乘10,且假设返回的是数组,就可以使用flatmap
const result = arr3.flatMap(item => [item * 10])
console.log(result)//[10,20,30]