目录
一、Iterator遍历器
1.作用
for...of本身是不能遍历没有遍历器Iterator的数据结构的,比如对象,但是我们给对象手动加上一个遍历器,他就能对这个对象遍历了。
2.原理
Symbol.iterator 是Symbol 对象的 iterator 属性,是一个特殊的Symbol值,因此,当它作为prototype对象属性名的时候,获取它的时候需要使用[ ]的形式: prototype[Symbol.iterator]
//数组:一个可遍历对象
let arr = ['a','b','c'];
//调用数组的Symbol.iterator()方法
let iter = arr[Symbol.iterator]();
iter.next();
//结果:{value: "a", done: false}
iter.next();
//结果:{value: "b", done: false}
iter.next();
//结果:{value: "c", done: false}
iter.next();
//结果:{value: undefined, done: true}
第1次调用next( )方法:返回数组的第1个元素:“a”,以及done的值为fasle,表示循环没有结束,继续遍历。
第2次调用next( )方法:返回数组的第2个元素:“b”,以及done的值还是为fasle,表示循环没有结束,继续遍历。
第3次调用next( )方法:返回数组的第3个元素:“c”,以及done的值依然为fasle,表示循环没有结束,继续遍历。
第4次调用next( )方法:返回的value值为undefined,以及done的值变成了true,表示遍历结束。
原来,for...of的原理就是:先调用可遍历对象的[Symbol.iterator]( )方法,得到一个iterator遍历器对象,然后就在遍历器上不断调用next( )方法,直到done的值为true的时候,就表示遍历完成结束了
3.自定义Iterator遍历器
//定义一个的Object对象
let obj = {
0:"我是0",
1:"我是1",
2:"我是2",
length:3,
//添加[Symbol.iterator]方法
[Symbol.iterator] : function() {
let _this = this;
let index = 0;
return {
next:() => {
let value = _this[index];
let done = (index >= _this.length);
index++;
return {value,done}
}
}
}
};
二、Generator函数
1.声明Generator函数
Generator函数与普通函数有两个区别:在function后加上“*”,多了一个关键字yield
function* a(b) {
let c = b;
yield c;
}
2.调用Generator函数
调用和普通函数区别比较大
要先定义个变量接收函数,在用这个变量调用next()方法获取到值,获取到的是一个对象,其中包括value和done,和遍历器中的next一致。每调用一次,就返回一次yield返回的值,直到没有yield。
function* a(b, f) {
let c = b;
let e = f;
yield c;
yield e;
}
let c = a(3, 8);
console.log(c);
console.log(c.next());
console.log(c.next());
打印结果:
3.Generator函数的执行步骤
Generator函数被调用后并不会一直执行到最后,它是先回返回一个生成器对象,然后hold住不动,等到生成器对象的next( )方法被调用后,函数才会继续执行,直到遇到关键字yield后,又会停止执行,并返回一个Object对象,然后继续等待,直到next( )再一次被调用的时候,才会继续接着往下执行,直到done的值为true。
4.yield
相当于普通函数中的return,但不同的是,return后面的不论是什么都不会执行,等同于终止;
yield后面还可以有其他yield,等同于暂停,每调用一次next就执行下一个yield。
有yield不妨碍Generator函数中有return,return还是相当于原来的作用
5.next方法
通过上面我们知道next可以被调用接收yield返回的值,他本身也可以传进参数,作用就是把这个值替换为上一个yield返回的值
6.yield*
用于在一个Generator函数中调用另一个Generator函数
如果一个Generator函数A执行过程中,进入(调用)了另一个Generator函数B,那么会一直等到Generator函数B全部执行完毕后,才会返回Generator函数A继续执行
7.作用
Generator函数可以根据需要,轻松地让函数暂停执行或者继续执行,利用Generator函数来实现异步操作
三、Set 和 WeakSet用法
1.Set是什么
Set是ES6给开发者带来的一种新的数据结构,就是值的集合
打印出来就是大括号中带有值
2.基本用法
let a = new Set([1, 2, 3, 4, "a", [1, 2, 3]], [5, 6, 7, 8]);
console.log(a);
类似于创建个对象,不同的是他要传进去一个数组,如果传了多个是没有用的,他只会解析第一个,得到数组后他会把数组“拆开”并放到Set中,但只会拆开第一层,深一层之后的会被留下来。
3.特点:成员值唯一
Set中不会有重复的值,姐就是说如果你传的数组中是有重复值的,Set会自动去掉重复的,
4.size属性
获取数值的个数,类似于数组的length。
let a = new Set([1, 2, 3, 4, "a", [1, 2, 3]], [5, 6, 7, 8]);
console.log(a);
console.log(a.size);
5.delete属性
跟他意思一样删除,删除指定的值,不是下标。成功会返回true,失败返回false
let a = new Set(["a", "b", "c"]);
console.log(a.delete(1));
console.log(a);
console.log(a.delete("a"));
console.log(a);
6.clear属性
清除一个Set中的所有值
let a = new Set(["a", "b", "c"]);
a.clear();
console.log(a);
7.has方法
判断Set中是否有传的值,有就true,没有就false
let a = new Set(["a", "b", "c"]);
console.log(a.has("a"));
console.log(a.has("d"));
8.enteries
调用就会返回一个Set的键值对
let a = new Set([1, 2, 3]);
console.log(a.entries());
9.keys和values方法
类似于enteries,返回的分别是键名的遍历器和键值的遍历器
有一点需要注意:Set中的键名和键值是相通的
10.forEach方法
let a = new Set([1, 2, 3]);
a.forEach(function (value, key) {
console.log(value, key);
});
11.用Set去重
let a = [1, 2, 3, 4, 5, 3, 1, 4, 6, 7, 6, 6, 6];
// [1,2,3,4,5,6,7]
let b = new Set(a);
console.log(b);
// 去重
let newA = Array.from(b);
// 转化成数组
console.log(newA);
12.Weakset
WeakSet结构同样不会存储重复的值,不同的是,它的成员必须是对象类型的值。
同样,WeakSet结构也提供了add( ) 方法,delete( ) 方法,has( )方法给开发者使用,作用与用法跟Set结构完全一致。
另一个不同点是:WeakSet 结构不可遍历。因为它的成员都是对象的弱引用,随时被回收机制回收,成员消失。所以WeakSet 结构不会有keys( ),values( ),entries( ),forEach( )等方法和size属性。
四、Map和WeakMap用法
1.Map是什么
ES6 提供了Map结构给我们使用,是键值对的集合,它跟Object对象很像,但是不同的是,它的key键名的类型不再局限于字符串类型了,它可以是各种类型的值;可以说,它比Object对象更加灵活了,当然,也更加复杂了
2.基本用法
Map类似Set,也要传数组,不同的是,数组中的每一项都要是数组,并且这些数组中只能是两个值,分别是属性和属性值,但不是对象中的写法,两者用逗号隔开
let a = new Map([
["name", "lisi"],
["age", "30"],
]);
console.log(a);
3.Set方法
用于设置一个键值对,包括修改和添加
let a = new Map([
["name", "lisi"],
["age", "30"],
]);
console.log(a);
a.set("name", "zhangsan");
console.log(a);
a.set("height", 180);
console.log(a);
4.get方法
用于获取一个键值对,传入键名获取
let a = new Map([
["name", "lisi"],
["age", "30"],
]);
console.log(a.get("name"));
5.delete方法
和Set一样,调用时传入键名
6.clear方法
和Set一样直接调用
7.has方法
和Set一样直接调用,调用时传入键名
8.entries方法
和Set一样直接调用
9.keys和values方法
和Set一样直接调用
10.forEach方法
和Set一样直接调用
11.size属性
let a = new Map([
["name", "lisi"],
["age", "30"],
]);
console.log(a.size);
12.WeakMap
WeakMap结构和Map结构很类似,不同点在于WeakMap结构的键名只支持引用类型的数据。哪些是引用类型的值呢?比如:数组,对象,函数。
跟Map一样,WeakMap也拥有get、has、delete方法,用法和用途都一样。不同地方在于,WeakMap不支持clear方法,不支持遍历,也就没有了keys、values、entries、forEach这4个方法,也没有属性size
五、ES6的Promise对象(重要)
1.目的即作用
当用ajax多次请求数据,并且后面的请求要依赖于前一次请求得到的数据时,那代码写出来就是回调函数层层嵌套(也叫回调地狱),不仅代码可读性差,不好调试,并且会浪费时间
那Promise对象就是为了解决这些问题的
2.基本用法
创建一个Promise实例就像创建其他对象实例一样
let a = new Promise(function (resolve, reject) {
});
只不过传进去的参数是一个匿名函数,匿名函数中有两个参数,一个是resolve方法,用于处理异步操作成功后的语句;一个是reject 方法,用于处理异步操作失败后的语句。
3.三种状态
pending:初始状态,一个promise实例刚刚创建好后就是这个状态
fulfilled:成功状态,resolve方法被调用时的promise实例状态
rejected:失败状态,reject方法被调用时的promise实例状态
注:
状态只能从 初始化 -> 成功 或者 初始化 -> 失败,不能逆向转换,也不能在成功fulfilled
和失败rejected之间转换。
4.then方法
在上面我们知道,一个promise实例中要有两个判断,判断处于哪种状态,那我们不能就做一个判断然后啥也不干吧,那没啥意义。肯定是判断完后要去执行什么。then就用来完成这个操作。
let a = new Promise(function (resolve, reject) {
if (true) {
resolve();
} else {
reject();
}
});
a.then(
function () {
console.log(111111);
},
function () {
console.log(22222);
}
);
里面传两个参数,皆为方法,第一个对应 resolve,如果调用了resolve,就执行这个函数中的内容;第二个对应reject,如果调用了reject,就执行这个函数中的内容。但因为有catch方法,一般then方法对应的就是调用resolve,也就是只有一个参数。
5.catch方法
和then类似。
let a = new Promise(function (resolve, reject) {
if (false) {
resolve();
} else {
reject();
}
});
a.then(function () {
console.log(11111);
}).catch(function () {
console.log(22222);
});
因为then和catch执行完都会返回一个promise实例对象,所以可以链式调用
6.案例
let z = new Promise(function (resolve, reject) {
if (true) {
resolve();
} else {
reject();
}
});
function A() {
console.log("A打印完了");
return "B,到你了";
}
function B(b) {
console.log(b);
console.log("B打印完了");
return "C,到你了";
}
function C(c) {
console.log(c);
console.log("C打印完了");
}
z.then(A)
.then(B)
.then(C)
.catch(function () {
console.log("失败了");
});
这就是模拟解决了开头的问题,函数B依赖于A的返回值,函数C依赖于B的返回值,只用了三个then方法就解决了,代码一点都不多。
7.Promise.all方法
Promise.all( )方法:接受一个数组作为参数,数组的元素是Promise实例对象,当参数中的实例对象的状态都为fulfilled时,Promise.all( )才会有返回。
这个方法有什么用呢?一般这样的场景:我们执行某个操作,这个操作需要得到需要多个接口请求回来的数据来支持,但是这些接口请求之前互不依赖,不需要层层嵌套。这种情况下就适合使用Promise.all( )方法,因为它会得到所有接口都请求成功了,才会进行操作。
8.Promise.race方法
Promise.race()方法:它的参数要求跟Promise.all( )方法一样,不同的是,它参数中的promise实例,只要有一个状态发生变化(不管是成功fulfilled还是异常rejected),它就会有返回,其他实例中再发生变化,它也不管了。
9ES7中的Async/await
Promise虽然跳出了异步嵌套的怪圈,解决了回调地狱的问题,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的Async/Await的出现就是为了解决这种复杂的情况
async用于申明一个function是异步的,返回一个promise对象,而await可以认为是async wait的简写,等待一个异步方法执行完成。async必须声明的是一个function,await必须在声明的函数内部使用
await是强制把异步变成了同步,这一句代码执行完,才会执行下一句
六、类基本用法
1.声明一个类
构造方法constructor是一个类必须要有的方法,默认返回实例对象;创建类的实例对象的时候,会调用此方法来初始化实例对象。如果你没有编写constructor方法,执行的时候也会被加上一个默认的空的constructor方法。构造方法里面的this,指向的是该类实例化后的对象
class Car {
constructor(number, color, money) {
this.color = color;
this.number = number;
this.money = money;
}
}
console.log(Car);
2.创建实例化对象
class Car {
constructor(color, number, money) {
this.color = color;
this.number = number;
this.money = money;
}
getnumber() {
console.log(this.number);
}
}
let HongQi = new Car("red", 4, "50w");
HongQi.getnumber();
console.log(HongQi);
3.类的自定义方法
class Car {
constructor(color, number, money) {
this.color = color;
this.number = number;
this.money = money;
}
getnumber() {
console.log(this.number);
}
}
let HongQi = new Car("red", 4, "50w");
HongQi.getnumber();
打印结果:
和构造函数并列
4.类的静态方法
这个有什么用呢,就是一个方法用这个声明了,就不能被实例对象调用了,只能被父类调用
class Car {
constructor(color, number, money) {
this.color = color;
this.number = number;
this.money = money;
}
getnumber() {
console.log(this.number);
}
static getcolor() {
console.log(this.color);
}
}
let HongQi = new Car("red", 4, "50w");
HongQi.getcolor();
调用就会报错,但用父类调用的话
Car.getcolor();
5.类的继承
用关键字extends实现继承,super,它相当于是父类中的this
class Chinacar extends Car {
constructor(color, number, money) {
this.color = color;
super(number);
this.money = money;
}
}
console.log(Chinacar.name);
使用super有几个要注意的事项:
- 子类必须在constructor方法中调用super方法
- 调用super( ),才可以使用this,否则报错
七、Moudule模块(重要)
1.目的
前端任务越来越重,为了方便维护,把大项目模块化
2.什么是模块
首先,要知道
目前还没有浏览器支持ES6的module模块。
解决方法:
- vscode安装live sever
模块Module:一个模块,就是一个对其他模块暴露自己的属性或者方法的文件。
3.导出Export
导出Export:作为一个模块,它可以选择性地给其他模块暴露(提供)自己的属性和方法,供其他模块使用。
4.导入Import
导入Import:作为一个模块,可以根据需要,引入其他模块的提供的属性或者方法,供自己模块使用。
5.基本用法
// 文件a
export let a = 18;
//文件b
import { a } from "./a.js";
console.log(a);
//html文件
<script src="b.js" type="module"></script>
6.批量导出
可以用大括号把想导出导入的变量包起来
//文件a
let j = 100;
let h = 200;
let l = 300;
export { j, h, l };
//文件b
import { j, h, l } from "./a.js";
console.log(j);
console.log(h);
console.log(l);
7.重命名导出的变量
在导入的时候可以给导入的变量换一个名
使用关键字as,可以实现给变量name更换名字为myname。最后正确输出myname的值:“前端君”。
8.整体导入
我们还可以使用星号*实现整体导入:
使用星号符*将模块B提供的所有属性和方法整体导入赋值给变量obj,我们可以点运算符来获取它的属性和方法。
9.默认导出
每个模块支持我们导出一个匿名方法,我们使用关键语句export default来实现,在导入文件中导入时可以随便取一个名字
// 文件a
export default function () {
console.log(4444);
}
// 文件b
import llllll from "./a.js";
llllll();
10.两点要注意的事项
- 声明的变量,对外都是只读的。不能在别的文件里
- 导入不存在的变量,会报错。