目录
Symbol 数据类型
ES6引入了一种新的数据类型Symbol,表示独一无二的值。它是JS的第七种数据类型,前六种分别是number、string、undefined、null、boolean、object。
特点:
- Symbol值是唯一的,解决命名冲突的问题;
- Symbol值不能与其他数据进行计算,包括字符串的拼接;
- for...in、for...of 不会遍历Symbol属性。
Symbol是函数,但并不是构造函数。创建一个Symbol数据类型:
const symbol=Symbol();
console.log(typeof(symbol));//symbol
Symbol的使用
1.作为对象的属性值
const sym=Symbol();
const obj={ name:'hua' };
obj[sym]='aaa'; //注意!只能通过属性选择器[]来添加和读取
console.log(obj, obj.sym, obj[sym]); // {name:'hua',Symbol():'aaa'} undefined aaa
for(let k in obj){
console.log(obj[k]); // hua
}
2.定义常量
const m=Symbol('my');
Set 数据结构
Set类似于数组,但值都是唯一不重复的。Set本身是一个构造函数,通过new Set()生成一个Set实例。
const set=new Set();
const set2=new Set([1,2,2,3,3,3]);
console.log(set2);//Set(3) {1,2,3} //不是数组,是Set数据结构
console.log([...set2]);//[1,2,3] //数组
Map 数据结构
Map类似于对象,也是键值对的集合,但它的键不限于字符串。也就是说Object结构提供了“字符串-值”的对应,Map结构提供了“值-值”的对应,是一种更为完善的Hash结构实现。
const map=new Map();
map.set(key,value);//添加
map.get(key);//通过键获取值
map.delete(key);//删除
Map的键实际上是和内存地址绑定的,只要内存地址不一样,就视为两个键。
const map=new Map();
map.set(['b'],1);
console.log(map.get(['b'])); //undefined
const a=['b'];
map.set(a,1);
console.log(map.get(a)); //1
promise ⭐
① 同步、异步
主线程执行同步任务完毕后,查询任务队列是否还有异步任务。
② 含义
Promise是ES6引入的异步编程的一种新解决方案。本身是一个构造函数。
简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
Promise 对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)。
注意:
- 一个 promise对象只能改变一次状态,成功或者失败后都会返回结果数据。
- 状态变化只有两种可能:从 pending 变为 fulfilled 和 从 pending 变为 rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。
- 后文中的 resolved 只指 fulfilled状态,不包括 rejected。
③ 基本使用
之前遇到的异步任务都是一次异步,如果需要多次调用异步函数,是通过“函数瀑布”来实现的,这种也称为“回调地狱”。
//1s后开始打印,共输出三次,每次间隔1s
setTimeout(() => {
console.log('first');
setTimeout(() => {
console.log('second');
setTimeout(() => {
console.log('third');
},1000)
},1000)
},1000)
在一个复杂的程序当中,用 "函数瀑布" 实现的程序无论是维护还是异常处理都是一件特别繁琐的事情,而且会让缩进格式变得非常冗赘。下面展示用Promise实现相同功能:
new Promise(function(resolve,reject){
setTimeout(() => {
console.log('first');
resolve();
},1000)
}).then(()=>
new Promise(function(resolve,reject){
setTimeout(() => {
console.log('second');
resolve();
}, 1000);
})
).then(function(){
setTimeout(() => {
console.log('third');
},1000)
})
解释一下上面的代码:
Promise 构造函数只有一个参数,这个参数是一个函数,该函数在构造之后会直接被异步运行,所以也被称为起始函数。起始函数有两个参数 resolve 和 reject,它们都是函数,由 JavaScript 引擎提供,不用自己部署。
- resolve函数的作用是,将Promise对象的状态从pending变为resolved(未完成->成功),在异步操作成功时调用,并将异步操作的结果作为参数传递出去;
- reject函数的作用是,将Promise对象的状态从pending变为rejected(未完成->失败),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去。
- resolve 和 reject 的作用域只有起始函数,不包括 then 以及其他序列。
- then方法可以接受两个回调函数作为参数,第一个回调函数是Promise对象的状态改变为 resoved 时调用,第二个回调函数是 Promise 对象的状态变为 rejected 时调用。其中第二个参数可以省略。
new Promise(function(resolve,reject){
let a=1;
let b=2;
if(b!=2){
reject('error!');
}
else{
resolve(a+b);
}
}).then(function(value){console.log('a+b='+value);},function(err){console.log(err)})
上面代码的then方法的第二个参数可以省略,然后使用catch方法,即:
new Promise(function(resolve,reject){
let a=1;
let b=2;
if(b!=2){
reject('error!');
}
else{
resolve(a+b);
}
}).then(function(value){
console.log('a+b='+value);
}).catch(function(err){
console.log(err);
})
Promise.prototype.catch()
方法是.then(null / undefined, rejuctoin)的别名,用于指定发生错误时的回调函数。
- 一般来说,不要在then()方法里定义reject状态的回调函数(即then的第二个参数),而是使用catch()方法。
- Promise 对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一catch个语句捕获。
- then块默认会向下顺序执行,return是不能中断的,可以通过throw来跳转至catch实现中断。
finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
const promise=new Promise((resolve,reject)=>{});
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
- finally方法的回调函数不接受任何参数
④ 综合案例
let a=+prompt('请输入一个数字');
var promise =new Promise(function(resolve,reject){
//To Do 要异步执行的事情,这个异步执行的事情有可能成功执行完毕,那么Promise将是fulfilled状态,如果执行失败则是rejected;
//下面测试代码,人为设置为rejected状态;
if(a!=0){resolve(a/2);}
else{reject("不能为0"); }
})
promise.then(//调用第一个then()
success=>{
console.log("异步执行成功,状态为fulfilled,成功后返回的结果是:"+success);
return(" 当前 success ");
},
error=>{
console.log("异步执行失败,状态为rejected,失败后返回的结果是:"+error);
return(" 当前 error ");
}
).then(
//调用第二个then() 因为调用第一个then()方法返回的是一个新的promise对象,此对象的状态由上面的success或者error两个回调函数的执行情况决定的:
//如果回调函数能正常执行完毕,则新的promise对象的状态为fulfilled,下面执行success2,如果回调函数无法正常执行,则promise状态为rejected;下面执行error2
success2=>{
console.log("第一个then的回调函数执行成功 成功返回结果:"+success2);
throw(" 当前 success2 ");//自定义异常抛出
},
error2=>{
console.log("第一个then的回调函数执行失败 失败返回结果:"+error2);
return(" 当前 error2 ");
}
).catch(err=>{
//当success2或者error2执行报错时,catch会捕获异常;
console.log("捕获异常:"+err);
}).finally(()=>{
console.log('end');
});
输出结果如下:
for...of
遍历器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作。遍历器对象本质上,就是一个指针对象。
ES6引入了 for...of 循环,作为遍历所有数据结构的统一的方法。
一个数据结构只要部署了 Symbol.iterator 属性,就被视为具有 iterator 接口,就可以用 for...of 循环遍历它的成员。也就是说, for...of 循环内部调用的是数据结构的 Symbol.iterator 方法。
const arr=['a','b','c'];
for(let v of arr){
console.log(v); //'a' 'b' 'c'
console.log(arr[v]); //undefined undefined undefined
}
for(let k in arr){
console.log(k); //'0' '1' '2'
console.log(arr[k]); //'a' 'b' 'c'
}
数组的键名是数字,但是for...in中是以字符串的形式返回的。for...in主要为遍历对象而设计,不适用于遍历数组。对于普通的对象,for...of 结构不能直接使用,不然会报错,必须部署了 Iterator 接口后才能使用。
Generator函数
Generator 函数是一个普通函数,但是有两个特征。
- function 关键字与函数名之间有一个星号;
- 函数体内部使用 yield 表达式,定义不同的内部状态;
- 调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,即上面提到的Iterator对象。
function* myGenerator(){
yield 1;
yield 'a';
return 'end';
}
let mg=myGenerator();
console.log(mg.next()); //{value:1,done:false}
console.log(mg.next()); //{value:'a',done:false}
console.log(mg.next()); //{value:'end',done:true}
必须调用遍历器对象的 next 方法,使得指针移向下一个状态,即下一个yield表达式(或return语句)。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
next方法:一般情况下,next方法不传入参数的时候,yield表达式的返回值是undefined,当next传入参数的时候,该参数会作为上一步yield的返回值。
function* myGenerator(){
let x=yield 1;
console.log(x); //next无参时undefined,有参时输出参数first
return ; //无参时undefined
}
let mg=myGenerator();
//console.log(mg.next());
console.log(mg.next('first'));
console.log(mg.next());
async函数⭐
async函数就是 Generator 函数的语法糖。
async function test1(){
return 1;
}
console.log(test1()); //Promise对象
async function test2(){
return Promise.resolve(2);
}
console.log(test2()); //Promise对象
async function test3(){
const p=Promise.resolve(3);
//下面两部分等价
p.then(data=>{
console.log(data);
})
//
const data=await p;
console.log(data);
}
console.log(test3()); //Promise对象
- return返回的不是一个Promise类型的对象,返回的结果就是resolved(fulfilled)状态的Promise。
- return返回的是一个Promise类型的对象,返回值成功就成功。
- await表达式相当于Promise.then成功的情况。
async function test3(){
//const data=await 3;
const data=await Promise.resolve(3);
console.log(data);
}
test3();
async function test4(){
const p=Promise.reject(4);
try{
const data=await p;
console.log('success '+data);
}
catch(err){
console.log('failure '+err);
}
}
test4();
- await 3相当于await Promise.resolve(3);
- Promise.catch异常的情况对应 try...catch。
类class ⭐
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过
class
关键字,可以定义类。
class lei{
constructor(age){
this.age=age;
}
fangfa(){
console.log('方法不需要写function');
}
}
let l=new lei(18);
console.log(l.age); //18
l.fangfa(); //方法不需要写function
- 通过class关键字创建类,类名首字母大写;
- 类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象;只要new生成实例化对象,就会自动调用该函数,即使不写也会自动生成;
- 生成实例化对象new不能省略;
- 方法不需要写function。
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum(){ console.log(this.x+this.y); }
father(){ return ('我是父亲'); }
}
class Son extends Father {
constructor(x,y){
super(x,y);
this.x=x;
this.y=y;
}
son() {
console.log(super.father()+'的儿子' );
}
sub(x,y){ console.log(this.y-this.x) }
}
let son = new Son(1, 2);
console.log(son); //Son {x: 1, y: 2}
son.sum(); // 3
son.son(); //我是父亲的儿子
son.sub(); //1
- 继承extends,super()。
- 必须先调用父类的构造函数才能写子类构造函数的this。
- constructor的this指向实例对象,方法的this指向调用者。
模块⭐
① 概念
模块化是指将一个大的程序文件,拆分成许多小的文件,然后将小文件组合起来。
优点:
- 防止命名冲突;
- 代码复用;
- 高维护性。
② 基本使用
ES6 模块不是对象,而是通过export
命令显式指定输出的代码,再通过import
命令输入。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量。
//分别暴露
export let name='hua';
export function fn(){
console.log('module');
}
//统一暴露
let name='hua';
function fn(){
console.log('module');
}
export{name,fn};
<script type="module">
//通用方式
import * as m from "../ES6/module初学";
console.log(m.name); //hua
m.fn(); //module
//解构方式
import {name,fn} from "../ES6/module初学";
console.log(name); //hua
fn(); //module
</script>
// 默认暴露(常用)
//默认暴露
export default{
age:18,
fun:function(){
console.log('默认暴露')
}
}
<script type="module">
//通用
import * as m from "../ES6/module初学";
console.log(m.default.age); //18
m.default.fun(); //默认暴露
//解构
import {default as m} from "../ES6/module初学";
console.log(m.age);
m.fun();
//简便,仅限默认暴露使用
import m from "../ES6/module初学";
comsole.log(m.age);
m.fun();
</script>
- 重名时可用 as 取别名
③ 应用
1) 按需加载:import() 可以在需要的时候,再加载某个模块
button.addEventListener('click',event=>{
import("../ES6/module初学")
.then(load=>{
load.open();
})
.catch(err=>{
console.log(err);
})
})
上面代码中,import() 方法放在 click 事件的监听函数之中,只有用户点击了按钮,才会加载这个模块。
2) 条件加载:根据不同的情况,加载不同的模块
if(xxx){
import('moduleA').then(...);
}else{
import('moduleB').then(...);
}
上面代码中,如果满足条件,就加载模块 A,不满足则加载模块 B。