ES6
ECMAScript
ECMA:European Computer Manufacturers Association
欧洲计算机制造商协会
为什么学ES6
ES6的版本变动内容最多,具有里程碑的意义
ES6加入许多新的语法特性,编程实现更简单、高效
ES6是前端发展趋势,就业必备技能
声明变量
let
let的特点
-
var可以重复声明变量,而let不行
-
块级作用域,例如在if else while for 等语句中
eg:
{ let girl = '夏嘉忆'; } console.log(girl); //结果:girl is not defined
-
不存在变量提升
eg:
console.log(song); var song = '浮夸'; //结果:undefined console.log(girl); let girl = '夏嘉忆'; //结果:报错
-
不影响作用域链
eg:
let name = '小涛'; function fn(){ console.log(name); } fn(); //结果:正常输出
const
声明常量 const name = '小涛';
注意事项:
-
一定要赋初始值,不然报错
-
一般常量使用大写(潜规则)
-
常量的值不能修改,不然报错
-
块级作用域
-
对于数组和对象的元素修改不算做对常量的修改,不会报错
eg:
const TEAM = ['UZI','MLXG','Ming','Letme']; TEAM.push('Meiko'); //结果:不会报错
变量的解构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值,这被称为解构赋值
-
数组的解构
eg:
const F4 = ['小沈阳','刘能','赵四','宋小宝']; let [xiao,liu,zhao,song] = F4; console.log(xiao); console.log(liu); console.log(zhao); console.log(song); //结果: /* 小沈阳 刘能 赵四 宋小宝 */
-
对象的解构
eg:
const zhao = { name:'赵本山', age:'不详', xiaopin:function(){ console.log("我可以演小品"); } }; let {name,age,xiaopin} = zhao; console.log(name); console.log(age); console.log(xiaopin); xiaopin();
模板字符串
ES6引入新的声明字符串的方式 ``
声明:
let str = `我也是一个字符串哦!`; console.log(str,typeof str); //结果:我也是一个字符串哦!string
特性:
-
内容中可以直接出现换行符
let str = `<ul> <li></li> <li></li> <li></li> <li></li> </ul>` //结果:正常输出
-
变量拼接
eg:
{ let lovest = `'魏翔`; let out = `${lovest}是我心目中最搞笑的演员`; } { let lovest = '魏翔'; let out = lovest+'是我心目中最搞笑的演员'; } //二者效果相同
对象的简便写法
ES6允许在大括号里面直接写入变量和参数,作为对象的属性和方法。这样书写更简洁
let name = '龙涛'; let change = function(){ console.log('我可以改变我自己'); } const person = { name, change, improve(){ console.log("我可以提高自己的技能"); } } //上面的写法等效于 const person = { name:name, change:change, improve:function(){ console.log("我可以提高自己的技能"); } }
箭头函数以及声明特点
ES6允许使用 箭头 (=>)定义函数
let fn = function(){ } //上面的写法等效于 let fn = ()=>{ }
箭头函数声明和之前的函数声明有什么区别:
-
this是静态的,this始终指向函数声明时所在作用域下的this的值
-
不能作为构造函数实例化对象
eg:
let Person = (name,age)=>{ this.name = name; this.age = age; } let me = new Person('xiao',30); console.log(me); //结果:报错
-
不能使用 arguments 变量
-
箭头函数的简写
-
省略小括号,当形参有且只有一个时
let add = n=>{ return n+n; } console.log(add(9); //结果:18
-
省略大括号,当代码只有一条语句的时候,此时 return 必须省略,而且语句的执行结果就是函数的返回值
let pow = (n)=>n*n; console.log(pow(8)); //结果:64
-
总结:
箭头函数适合与this无关的回调,定时器、数组的方法回调。
箭头函数不适合与this有关的回调,事件回调,对象的方法。但不适合不是不能.
函数参数默认值
ES6允许给函数参数赋值初始值
1.形参初始值(具有默认值的参数,一般位置要靠后(潜规则))
function(a.b.c=10){ return a+b+c; } let result = add(1,2); console.log(result); //结果:13
2.与解构赋值结合
eg:
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参数
ES6引入 rest 参数,用于获取函数的实参,用来代替 arguments
用法:function(a,b,...args)
//ES5获取实参的方式 function(){ console.log(arguments); } date('渐变大理石','迈阿密','多普勒'); //rest参数 function date(...args){ console.log(args); } date('渐变大理石','迈阿密','多普勒');
rest 参数必须要放到参数最后
扩展运算符
... 扩展运算符能将数组转换为逗号分隔的参数序列
eg:数组的合并
const kuaizi = ['王太利','肖央']; const fenghuang = ['曾毅','玲花']; const zuixuanxiaopingguo = [...kuaizi,...fenghuang]; console.log(zuixuanxiaopingguo); //结果:['王太利','肖央','曾毅','玲花']
eg2:数组的克隆
const sanzhihua = ['E','G','M']; const sanyecao = [...sanzhihua]; console.log(sanyecao); //结果:["E","G","M"]
eg3:将伪数组转为真正的数组
const divs = document.querySelectorAll('div'); const divArr = [...divs]; console.log(divArr); //结果:[div,div,div]
Symbol基本使用
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值,是一种类似于字符串的数据类型
特点:
-
Symbol的值是唯一的,用来解决命名冲突的问题
-
Symbol值不能与其他数据进行运算
-
Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
创建Symbol
let s = Symbol();
Symbol.for创建
let s = Symbol.for('小涛');
迭代器Iterator
一种接口,为各种不同的数据结构提供统一的访问机制
任何数据结构只要部署Iterator接口,就可以完成遍历操作
ES6创造了一种新的遍历命令 for...of 循环,Iterator接口主要供for of消费
原生具备Iterator接口的数据(可用for of遍历)
Array
Arguments
Set
Map
String
TypedArray
NodeList
const dao = ['刺刀','爪子刀','蝴蝶刀']; for(let v of dao) { console.log(v); } //结果:刺刀 爪子刀 蝴蝶刀 const dao = ['刺刀','爪子刀','蝴蝶刀']; for(let v in dao) { console.log(v); } //结果: 0 1 2
生成器(generator)
是ES6提供的一种异步编程解决方案,语法行为与传统函数完全不同
生成器是一个特殊的函数
以前解决异步编程:纯回调函数
生成和执行均与普通函数有区别:
//生成 function *gen(){ console.log("hello generator"); } //执行 let iterator = gen(); iterator.next();
yield
函数代码的分隔符
function* gen(){ console.log('哈哈'); yield '1'; console.log('呵呵'); yield '2'; console.log('嘿嘿'); yield '3'; console.log('小涛'); }
两个yield之间的内容就是iteartor.next()执行一次的内容
Promise
Promise 是ES6引入的异步编程的新解决方案。语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
使用promise:
//实例化Promise对象 const p = new Promise(function(resolve,reject){ setTimeout(() => { let data = '数据库中用户的数据'; //resolve resolve(data); }, 1000); }); //调用promise对象的then方法 p.then(function(value){ console.log(value); },function(reason){ console.log(reason); }) //如果失败: const p = new Promise(function(resolve,reject){ setTimeout(() => { let err = '数据读取失败'; reject(err); }, 1000); }); p.then(function(value){ console.log(value); },function(reason){ console.log(reason); }) //结果:执行 console.log(reason),即‘数据读取失败’
Promise封装读取文件
一般读取文件:
const fs = require('fs'); fs.readFile('../ES6practice/resources/text.txt', (err, data) => { if (err) throw err; console.log(data.toString()); });
使用Promise封装:
const p = new Promise(function (resolve, reject) { fs.readFile("../ES6practice/resources/text.txt", (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) =>{ const xhr = new XMLHttpRequest(); xhr.open("GET","https://api.apiopen.top/getJoke"); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4) { if(xhr.status <= 200 && xhr.status <300){ resolve(xhr.response); }else{ //如果失败 reject(xhr.status); } } } }); //指定回调 p.then(function(value){ console.log(value); },function(reason){ console.error(reason); })
then
Promise封装的ajax请求如果成功,那么then方法返回的就是一个Promise对象
如果在resolve执行后返回一个非对象的值,则判定Promise执行成功
链式调用
在then方法中可以嵌套多个异步任务
p.then(value=>{ }).then(value=>{ });
Promise读取多个文件
普通读取文件方法:
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,data1)=>{ resolve(data); }); }); p.then(value=>{ return new Promise((resolve,reject)=>{ fs.readFile('./resources/悯农.md',(err,data2)=>{ resolve([value,data]); }); }); }).then(value=>{ return new Promise((resolve,reject)=>{ fs.readFile('./resources/无题.md',(err,data3)=>{ //压入 value.push(data); resolve(value); }); }); }).then(value=>{ constole.log(value.join('\r\n')); })
Promise对象的 catch 方法
const p = new Promise((resolve,reject)=>{ setTimeout(() => { reject("出错啦"); }, 1000); }); // p.then(function(value){},function(reason){ // console.log(reason); // }); p.catch(function(reason){ console.warn(reason); })
其中catch和注释中then方法在此时的作用是相同的,即catch可以不关注promise是否成功,而只关注错误后怎么处理
集合(Set)
ES6提供了新的数据结构Set(集合)。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用【扩展运算符】和【for...of...】进行遍历,集合的属性和方法:
-
size 返回集合的元素个数
-
add 增加一个新元素,返回当前集合
-
delete删除元素,返回boolean值
-
has 检测集合中是否包含某个元素,返回boolean值
-
clear 清空集合
集合的声明:
let s = new Set(); let s2 = new Set(['1','2','3','4','5']);
常见操作:
1.数组去重
let result = [...new Set(arr)]; console.log(result);
2.交集
let arr2 = [4,5,6,5,6]; let result = [...new Set(arr)].filter(item=>{ let s2 = new Set(arr2); if(s2.has(item)){ return true; }else{ return false; } }); console.log(result); //简化版 let result = [...new Set(arr)].filter(item=> new Set(arr2).has(item));
3.并集
let union = [...new Set([...arr,...arr2])]; console.log(union);
4.差集
let diff = [...new Set(arr)].filter(item=> !new Set(arr2).has(item));
Map
ES6提供了Map数据结构,它类似于对象,也是键值对的集合。但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用【扩展运算符】和【for...of...】进行遍历。Map的属性和方法:
-
size 返回map的元素个数
-
set 增加一个新元素,返回当前Map
-
get 返回简明对象的键值
-
has 检测Map中是否包含某个元素,返回boolean值
-
clear 清空集合,返回 undefined
声明Map:
let m = new Map();
class类
ES6提供了更接近传统语言的写法,引入了Class(类)这个概念,作为对象的模板。通过class关键字,可以定义类。基本上,ES6的class可以看作只是一个语法糖,它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
知识点:
-
class声明类
-
constructor定义构造函数初始化
-
extends继承父类
-
super调用父级构造方法
-
static定义静态方法和属性
-
父类方法可以重写
浅尝一下声明class:
class Phone{ constructor(brand,price){ this.brand = brand; this.price = price; } call(){ console.log("我可以打电话!!"); } } let xiaomi = new Phone('小米',5499); xiaomi.call();
class里的静态成员
static标注的属性和方法属于类而不属于实例对象
class Phone{ static name = '手机'; static change(){ console.log("我可以改变世界"); } }
class的继承
参考java
浅尝一下继承:
class Phone{ constructor(brand,price) { this.brand = brand; this.price = price; } static name = '手机'; static change(){ console.log("我可以改变世界"); } call(){ console.log("我可以打电话"); } } class SmartPhone extends Phone{ constructor(brand,price,color,size) { super(brand,price); this.color = color; this.size = size; } photo(){ console.log("拍照"); } } const xiaomi = new SmartPhone('小米',5499,'黑色','4.7inch'); xiaomi.call(); xiaomi.photo();
子类对父类方法的重写
沿用上面浅尝的继承:
class Phone{ constructor(brand,price) { this.brand = brand; this.price = price; } static name = '手机'; static change(){ console.log("我可以改变世界"); } call(){ console.log("我可以打电话"); } } class SmartPhone extends Phone{ constructor(brand,price,color,size) { super(brand,price); this.color = color; this.size = size; } photo(){ console.log("拍照"); } call(){ console.log("我可以视频通话"); } } const xiaomi = new SmartPhone('小米',5499,'黑色','4.7inch'); xiaomi.call(); xiaomi.photo();
在js中,子类不能直接调用父类的同名方法
get、set方法,参考java
class Phone{ get price(){ console.log("价格属性被修改了"); } set price(new Val){ console.log('价格属性被修改了'); } }
ES6的数值扩展
-
Number.EPSILON 是 JavaScript表示的最小精度
EPSILON属性的值接近于2.22E-16
-
Number.isFinite 检测一个数值是否为有限数
-
Number.isNaN 检测一个数值是否为有限数
-
Number.parseInt Number.parseFloat 字符串转整数
-
Number.isInteger 判断一个数是否为整数
-
Math.trunc将数字的小数部分抹掉
-
Math.sign 判断一个数到底是整数,负数,还是0
-
二进制和八进制
let b = 0b1010;//二进制 let o = 0o777;//八进制 let d = 100;//十进制 let x = 0xff;//十六进制
对象方法的扩展
Object.is 判断两个值是否完全相等
与===
的区别:Object.is认为两个NaN是相等的,===
认为两个NaN是不等的
Object.assign 对象的合并
const config1 = { host:'localhost', port:3306, name:'root', pass:'root' } const config2 = { host:'http://baidu.con', port:33060, name:'xiaotao', pass:'iloveyou' } console.log(Object.assign(config1,config2));
如果出现重名,后面的对象的属性会覆盖前面的属性
Object.setPrototypeOf 设置原型对象
Object.getPrototypeOf
const school = { name:'xiaotao' } const cities = { zhuzhi:['内江','成都'] } Object.setPrototypeOf(school,cities);
Object.setPrototypeOf 作用:将前一个对象的原型设置为后一个对象,但是不 推荐这样做
Object.getPrototypeOf 作用:获取一个对象的原型对象
模块化
将一个大的文件,拆分为许多小的文件,然后将小文件组合起来
模块化的好处
-
防止命名冲突
-
代码复用
-
高维护性
模块化语法
<script type="module"> </script>
模块功能主要由两个命令构成:export 和 import
export 用于规定模块的对外接口
import 用于输入其他模块提供的功能
暴露
暴露:将属性、函数等提供给其他文件使用
模块暴露数据有三种方式:分别暴露、统一暴露、默认暴露
分别暴露:
export let name = 'xiaotao'; export function teach(){ console.log("我可以教你做人"); }
统一暴露:
export{name,teach};
默认暴露:
export default { name :'xiaotao', change:function(){ console.log("我可以改变自己"); } }
引入
1.通用方式:
import * as m1 from "./src/js/m1.js";
2.解构赋值形式
import {name,teach} from "./src/js/m1.js;"
如果两个import中含有相同的属性,那么对其中一个进行起别名处理
import {name,teach} from "./src/js/m1.js"; import {name as mingzi,change} from "./src/js/m2.js"; //应对默认暴露时: import {default as m3} from "./src/js/m3.js"; console.log(m3);
3.简便形式 (只针对默认暴露)
import m3 from "./src/js/m3.js";
模块化方式除了直接写在script标签中,还可以设置一个js格式的入口文件
然后从script标签中引入该文件
<script src="./src/js/app.js" type="module"></script>
入口文件:
import * as m1 from "./m1.js";
babel对ES6模块化代码转换
bable可以将新特性的语法转化为ES5能识别的语法
安装工具:
1.babel-cli
2.babel -preset-env
3.browserify(项目中一般选择用webpack)
ES7
引入inlcludes判断数组中是否含有某元素,返回布尔值
console.log(name.includes('xiaotao'));
引入 ** 进行幂运算
例:2的10次方
console.log(2 ** 10);
效果和Math.pow(2,10)
一样
ES8
async 和 await
async和await两种语法结合可以让异步代码像同步代码一样
async函数
async函数返回值是一个promise对象
promise对象的结果由async函数执行的返回值决定
async function fn(){ //只要函数返回的不是一个promise对象,则async函数返回的是一个成功的 promise对象 //但如果抛出错误,返回的结果是一个错误的对象 //如果返回的是一个promise对象,例: return new Promise((resolve,reject)=>{ resolve('成功的数据'); }); //那么返回什么promise对象,async就返回什么promise对象 }
await表达式
-
await必须放在async函数中
-
await右侧的表达式一般为promise对象
-
await返回的是promise成功的值
-
await的prmise失败了,就会抛出异常,需要通过try...catch 捕获处理
//创建Promise对象 const p = new Promise((resolve,reject)=>{ resolve("成功的值"); }); //await要放在 async 函数中 async function main(){ let result = await p; console.log(result); } //调用函数 main();
如果失败:
//创建Promise对象 const p = new Promise((resolve,reject)=>{ // resolve("成功的值"); reject("失败啦"); }); //await要放在 async 函数中 async function main(){ try{ let result = await p; console.log(result); }catch(e){ console.log(e); } } //调用函数 main();
async和await结合读取文件:
const fs = require('fs'); const { resolve } = require('path'); const { promisify } = require('util'); //读取text function readHello() { return new Promise((resolve, reject) => { fs.readFile("./resources/text.txt", (err, data) => { //如果失败 if (err) { reject(err); } //如果成功 resolve(data); }); }); } function readWorld() { return new Promise((resolve, reject) => { fs.readFile("./resources/text2.txt", (err, data) => { //如果失败 if (err) { reject(err); } //如果成功 resolve(data); }); }); } //声明一个async函数 async function main() { //获取text内容 let hello = await readHello(); //获取text2内容 let world = await readWorld(); console.log(hello.toString()); console.log(world.toString()); } main();
async和await结合发送AJAX请求:
//发送AJAX请求,返回的结果是promise对象 function sendAJAX(url){ 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); } } } }) } //async与await测试 async function main(){ //发送AJAX请求 let result = await sendAJAX("https://api.apiopen.top/getJoke"); console.log(result); } main();
对象方法扩展
Object.values()方法返回一个给定对象的所有可枚举属性的值
Object.entries()方法返回一个给定对象自身可遍历性[key,value]的数组
Object.getOwnPropertyDescriptors()
该方法返回指定对象所有自身属性的描述对象
ES9
扩展运算符与rest参数
rest参数与spread扩展运算符在ES6中已经引入,不过ES6中只针对于数组,在ES9中为对象提供了像数组一样的 rest参数和扩展运算符
正则扩展
正在观察是否深入学习,待补充
ES10
对象扩展方法
Object.fromEntries()
该方法创建一个对象
参数为一个Map或者二维数组
const m = new Map(); m.set("name","xiaotao"); const result = Object.createEntries(m); console.log(result);
字符串方法扩展
trimStart 与 trimEnd
分别用来清除字符串左侧空白和右侧空白
let str = ' iloveyou '; console.log(str); console.log(str.trimStart()); console.log(str.trimEnd());
数组方法扩展
flat 与 flatMap
flat 将多维数组转化为低维数组
const arr = [1,2,3,4,[5,6]]; console.log(arr.flat());
flat中可以传入数字,用来表示降几维(深度),默认为1
flatMap
const arr = [1,2,3,4]; const result = arr.flatMap(item=>[item*10]);
Symbol扩展
description属性
对symbol对象的描述
let s = Symbol('xiaotao'); console.log(s.description);
ES11
私有属性
在属性前加#
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 boy = new Person('xiaotao',19,'50kg'); boy.intro();
Promise.allSettled
返回的是一个成功的promise对象
const p1 = new Promise((resolve,reject)=>{ setTimeout(()=>{ resolve('商品数据-1'); },1000) }); const p2 = new Promise((resolve,reject)=>{ setTimeout(()=>{ //resolve('商品数据-2'); reject('出错啦!'); },1000) }); const result = Promise.allSettled([p1,p2]); console.log(result);
Promise.all方法类似,不同的是,all方法如果其中有一个是失败,则返回的promise对象就是失败的
可选链操作符
?.
判断有没有这个属性,有的话再继续向下读取属性
function main(config){ const dbHost = config?.db?.host; console.log(dbHost); } main({ db:{ host:'192.168.1.100', username:'root' }, cache:{ host:'192.168.1.200', username:'admin' } })
动态引入import
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>加载</button> <script src="./app.js"></script> </body> </html>
app.js:
const btn = document.querySelector('button'); btn.onclick = function () { import('./hello').then(module => { module.hello(); }); }
hello.js:
export function hello() { alert('Hello'); }
BigInt
ES11引入了新的数据类型:大整形
用法:数字后加 n
let n = 520n;
BigInt只能和BigInt进行运算
绝对全局对象
globalThis,这个始终指向全局对象
console.log(globalThis);