参考书籍:http://es6.ruanyifeng.com/
参考视频:https://www.bilibili.com/video/av47304735
全部篇章:
ES6知识点归纳(一)——var、let、const、箭头函数、函数参数的默认值、ES6模版字符串
ES6知识点归纳(二)——对象解构、数组解构、for of循环、新增的方法、剩余参数、扩展运算符
ES6知识点归纳(三)——Promise、Symbol、模块(Modules)、class继承、Iterator、Generator、Proxy、Set、WeakSet、Map、WeakMap
其他资料:https://www.jianshu.com/p/39adf6ab8ad1
let userName;
const usersPromise = axios.get("https://api.github.com/users");
usersPromise
.then(response => {
console.log(response);
userName = response.data[0].login;
return axios.get(`https://api.github.com/users/${userName}/repos`);
})
.then(res => {
console.log(res.data);
})
.catch(err => {
console.error(err);
});
const p = new Promise((resolve, reject) => {
resolve("success"); //成功就立即返回success
});
//监听这个promise
p.then(data => {
console.log(data);
}); //打印返回的数据
const p = new Promise((resolve, reject) => {
reject(Error("failed")); //此处加Error()可以指明错误发生在这行
});
//监听这个promise
p.then(data => {
console.log(data);
}).catch(err => {
console.error(err);
});
//不用catch会报Uncaught (in promise) failed
const usersPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(["uOne", "uTwo"]);
}, 2000);
});
const moviePromise = new Promise((resolve, reject) => {
setTimeout(() => {
// resolve([{ name: "hi", rating: 9.2 }]);
reject(Error("failed"));
}, 500);
});
Promise.all([usersPromise, moviePromise])
.then(res => {
console.log(res); //结果的数组与传入的数组顺序一样
})
.catch(err => {
console.log(err);
});
const usersPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(["uOne", "uTwo"]);
}, 2000);
});
const moviePromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(Error("failed"));
}, 500); //此处时间较短,先返回moviePromise
});
Promise.race([usersPromise, moviePromise])
.then(res => {
console.log(res); //结果的数组与传入的数组顺序一样
})
.catch(err => {
console.log(err);
});
//最后结果Error: failed,因为第一个返回的结果是moviePromise
const usersPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject(Error("failed"));
}, 2000);
});
const moviePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve([{ name: "hi", rating: 9.2 }]);
}, 500); //此处时间较短,先返回moviePromise
});
Promise.race([usersPromise, moviePromise])
.then(res => {
console.log(res); //结果的数组与传入的数组顺序一样
})
.catch(err => {
console.log(err);
});
//最后结果打印了moviePromise返回的结果
来看一下这样一种情景
const classRoom = {
Alfred: { grade: 80, gender: "male" },
Raymond: { grade: 100, gender: "male" },
Raymond: { grade: 150, gender: "male" }
};
console.log(classRoom);
//由于两个Raymond的属性名重复,因此后面那个会覆盖前面的一个
const greyson = Symbol();
const student = Symbol();
console.log(greyson); //Symbol()
console.log(student); //Symbol()
console.log(typeof greyson, typeof student); //symbol symbol
console.log(greyson == student); //false
console.log(greyson === student); //false
const greyson = Symbol("greyson");
const student = Symbol("student");
console.log(greyson); //Symbol(greyson)
console.log(student); //Symbol(student)
因此,上方的情景可以改写为
const classRoom = {
[Symbol("Alfred")]: { grade: 80, gender: "male" },
[Symbol("Raymond")]: { grade: 100, gender: "male" },
[Symbol("Raymond")]: { grade: 150, gender: "male" }
};
console.log(classRoom);
Object.getOwnPropertySymbols()
来访问const classRoom = {
[Symbol("Alfred")]: { grade: 80, gender: "male" },
[Symbol("Raymond")]: { grade: 100, gender: "male" },
[Symbol("Raymond")]: { grade: 150, gender: "male" }
};
for (var key in classRoom) {
console.log(key); //什么都没打印
}
console.log(Object.keys(classRoom)); //空数组
console.log(Object.getOwnPropertyNames(classRoom)); //空数组
//正确打印classRoom
const syms = Object.getOwnPropertySymbols(classRoom);
console.log(syms);
//获取属性值
const syms2 = Object.getOwnPropertySymbols(classRoom).map(
sym => classRoom[sym]
);
console.log(syms2);
[]
,如果使用.
访问,sym 代表的 Symbol 会被当成字符串,就等同于使用了classRoom['sym']
,最终结果返回的值都是 undefined在过去,我们要把代码模块化的时候,可能需要很多 script 标签,如果引用很多模块的话,一方面会影响访问的速度,另一方面还会定义很多全局变量,可能会导致命名冲突
<body>
<h1>Hello,world</h1>
<script src="user.js"></script>
<script src="stores.js"></script>
</body>
为了结局这一问题 ES6 给我们提供了一个模块机制。
a.js
//默认导出
const apiKey = "abc123";
export default apiKey;
//命名导出
export const apiKey = "abc123";
export const age = 12;
export function hi() {
console.log("hi");
}
或
const apiKey = "abc123";
const age = 12;
function greet() {
console.log("hi");
}
export { apiKey as Key, age, greet };
//as可以将apiKey命名外另外的你想要导出的名字,引入时要使用as后的名字
b.js
//使用默认导出时
import apiKey from "./a.js"; //这里apiKey是自己命名的,可以修改
console.log(apiKey); //abc123
import { apiKey, age } from "./a.js";
当引入的变量名和模块里已有的变量名冲突的话,可以使用 as 重命名
import { Key as apiKey, age } from "./a.js";
注意
app.js
//导入其他包的方法或变量
import { uniq } from "lodash"; //引用lodash里面的uniq方法,这种是命名导出
import moment from "moment"; //导入默认的信息
index.html
<body>
<h1>Hello,worldh1>
<script src="./app.js">script>
body>
注意:
function User(name, email) {
this.name = name;
this.email = email;
}
User.prototype.info = function() {
console.log(`Hi.I'm ${this.name}`);
};
const Alfred = new User("Alfred", "[email protected]");
const Raymond = new User("Raymond", "[email protected]");
//实例化之后重写原型对象属性
User.prototype.info = function() {
console.log(`Hi.I'm ${this.name},my email is ${this.email}`);
};
User.prototype.description = function() {
console.log("hi");
};
console.log(Alfred);
class User {
//这个类的内容
}
const User = class {
//这个类的内容
};
const User = class {
//类的构造函数
constructor(name, email) {
this.name = name;
this.email = email;
}
info() {
console.log(`Hi.I'm ${this.name},my email is ${this.email}`);
}
//静态方法
static description() {
console.log("description");
}
set github(value) {
this.githubName = value;
}
get github() {
return `https://github.com/${this.githubName}`;
}
};
const Alfred = new User("Alfred", "[email protected]");
const Raymond = new User("Raymond", "[email protected]");
Alfred.githubName = "Niccce";
// Alfred.description();//Alfred.description is not a function
User.description();
console.log(Alfred);
console.log(Alfred.github);
let methodName = "info";
const User = class {
constructor(name, email) {
this.name = name;
this.email = email;
}
[methodName]() {
console.log(`Hi.I'm ${this.name},my email is ${this.email}`);
}
};
const Alfred = new User("Alfred", "[email protected]");
Alfred.info();
class Animal {
constructor(name) {
this.name = name;
this.belly = [];
}
eat(food) {
this.belly.push(food);
console.log(this.belly);
}
}
class Dog extends Animal {
constructor(name, age) {
super(name); //调用父类的构造函数
this.age = age;
}
bark() {
console.log("barking");
}
}
const lucky = new Dog("lucky", 2);
lucky.eat("meat");
console.log(lucky);
class Animal {
constructor(name) {
this.name = name;
this.belly = [];
}
speak() {
console.log(`Hi,I'm ${this.name}`);
}
}
class Dog extends Animal {
constructor(name, age) {
super(name); //调用父类的构造函数
this.age = age;
}
bark() {
console.log("barking");
}
speak() {
console.log(`Bark,I'm ${this.name}`);
}
}
const lucky = new Dog("lucky", 2);
lucky.speak(); //Bark,I'm lucky
简单例子
class MyArray extends Array {
constructor() {
super();
}
}
const colors = new MyArray();
colors[0] = "red";
console.log(colors.length); //1
colors.length = 0;
console.log(colors[0]); //undefined
简单运用
class singers extends Array {
constructor(name, ...item) {
//在这里把剩余参数都写在了item里
super(...item); //在这里是把item里的元素都扩展出来
this.name = name;
}
add(singer) {
this.push(singer);
}
}
const movies = new singers(
"favorite singers",
{ name: "Raymond", no: 1 },
{ name: "Alfred", no: 2 },
{ name: "Greyson", no: 3 }
);
movies.push({ name: "Fred", no: 4 });
console.log(movies);
console.table(movies);
遍历器是一种接口,为各种不同的数据结构提供统一的访问机制。
const colors = ["red", "green", "yellow"];
console.log(colors);
const iterator = colors[Symbol.iterator]();
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//colors.entries()这个方法返回的是元素的属性名可属性值
const colors = ["red", "green", "yellow"];
console.log(colors.entries());
const iterator = colors.entries();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
const colors = ["red", "green", "yellow"];
console.log(colors.values());
const iterator = colors.values();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//colors.keys()返回的是元素的索引值
const colors = ["red", "green", "yellow"];
console.log(colors.keys());
const iterator = colors.keys();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
编写自己的遍历器
Array.prototype.Myvalues = function() {
let i = 0; //记录当前元素的位置
let items = this;
return {
//这个方法返回一个对象,即遍历器
next() {
const done = i >= items.length;
const value = done ? undefined : items[i++];
return {
value,
done
};
}
};
};
const colors = ["red", "green", "yellow"];
const iterator = colors.Myvalues();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
目前 JavaScript 的函数都是从上到下依次执行直到结束,而 Generator 函数可以开始暂停开始暂停,也可以在调用当中传入另外的参数。
*
yield
语句定义不同的内部状态(‘yield’意思是产出)function* listColors() {
yield "red"; //有点类似函数的返回值,但这个yield是本次执行这个函数的返回值
yield "green";
yield "yellow";
}
const colors = listColors();
console.log(colors); //打印说是一个Generator,状态是suspended
console.log(colors.next());
console.log(colors.next());
console.log(colors.next());
console.log(colors.next());
console.log(colors);//状态编程了closed
function* num(){
let i=0;
yield i;
i++;
yield i;
i++;
yield i;
}
const colors=num();
console.log(colors.next());
console.log(colors.next());
console.log(colors.next());
console.log(colors.next());
const singers = [
{ name: "Raymond", no: 1 },
{ name: "Alfred", no: 2 },
{ name: "Greyson", no: 3 }
];
function* loop(arr){
for(const repo of arr){
yield repo;
}
}
const repoGen=loop(singers);
console.log(repoGen);
console.log(repoGen .next());
console.log(repoGen .next());
console.log(repoGen .next());
console.log(repoGen .next());
<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
function ajax(url){
axios.get(url).then(res=>userGen.next(res.data));//4.请求完成后,将所得数据传给next方法继续进行Generator的下一步,得到的数据也会存到users中
}
function* steps(){
const users=yield ajax('https://api.github.com/users');//3.开始请求GitHub用户,此时steps函数暂停了
console.log(users);
const firstUser=yield ajax(`https://api.github.com/users/${users[0].login}`);
console.log(firstUser);
const followers=yield ajax(firstUser.followers_url);
console.log(followers);
}
const userGen=steps();//1.调用steps生成Generator
userGen.next();//2.调用Generator的next方法开始执行Generator
script>
var proxy = new Proxy(target,handler);
,target是我们要代理的目标对象,handler是一个对象,它包含了我们想要重写的一些操作,这个对象里的方法我们称之为trap,这些方法详见MDNconst person={name:'Alfred',age:30};
const personProxy=new Proxy(person,{
get(target,key){
return target[key].toUpperCase();
},//改变了获取属性值的默认操作
set(target,key,value){
if(typeof value ==='string'){
target[key]=value.trim();//去掉属性值左右的空格
}
}
});
personProxy.name='Raymond';
console.log(personProxy);//Proxy {name: "Raymond", age: 30}
console.log(personProxy.name);//RAYMOND
personProxy.sayHi=' Hi,Alfred ';
console.log(personProxy.sayHi);//HI,ALFRED
const phonenumHandler = {
set(target, key, value) {
target[key] = value.match(/[0-9]/g).join("");
},
get(target, key) {
return target[key].replace(/(\d{3})(\d{4})(\d{4})/, "$1-$2-$3");
}
};
const phoneNum = new Proxy({}, phonenumHandler);
phoneNum.home = "131 2222 3333";
console.log(phoneNum);//Proxy {home: "13122223333"}
console.log(phoneNum.home);//131-2222-3333
match()方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
join()方法用于把数组中的所有元素通过指定的分隔符进行分隔,然后放入一个字符串。
replace()方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
const person = { id: 2 };
person.ID = 2;
person.iD = 2;
person.id = 2;
为了避免用户花费很多时间来排除这样一些bug,我们可以通过proxy来对用户的操作进行处理或保护
const safeHandler = {
set(target, key, value) {
const likeKey=Object.keys(target).find(k=>k.toLowerCase()===key.toLowerCase());//用来寻找对象属性中和我们定义的属性很相似,只是大小写不同的属性
if(!(key in target)&& likeKey){
//如果我们要设置的这个属性不在我们的target里,并且它有相似的key
throw new Error(`Oops!Looks like we already have a property ${key} but with the case of ${likeKey}`);
}
target[key]=value;
}
};
const safetyProxy = new Proxy({ id: 2 }, safeHandler);
safetyProxy.ID = 5;
Object.keys(obj)
find()
简单用法:
add()
添加元素,size
获取Set长度const color = new Set();
color.add("red");
color.add("green");
color.add("yellow");
console.log(color);//Set(3) {"red", "green", "yellow"}
//调用构造函数时初始化
const fruits=new Set(['apple','banana']);
console.log(fruits);//Set(2) {"apple", "banana"}
const color = new Set();
color.add("red");
color.add("green");
color.add("yellow");
console.log(color.size); //获取长度
color.add("5");
color.add(5);
console.log(color); //Set(5) {"red", "green", "yellow", "5", 5}
color.add("yellow");
console.log(color); //Set(5) {"red", "green", "yellow", "5", 5}
delete()
删除元素,has()
检验一个元素是否存在,clear()
清楚所有元素const color = new Set();
color.add("red");
color.add("green");
color.add("yellow");
color.delete('red');
console.log(color);//Set(2) {"green", "yellow"}
console.log(color.has('red'));//false
console.log(color.has('green'));//true
color.clear();
console.log(color);//Set(0) {}
const color = new Set();
color.add("red");
color.add("green");
color.add("yellow");
console.log(color.values());
const iterator=color.values();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
//因为Set部署了遍历器接口,因此可以用for of循环
for(let Color of color){
console.log(Color);
}
color.forEach((item,key,ownSet) => {
console.log(item,key,ownSet);
});
数组去重
const nums = [1, 2, 3, 4, 5, 5, 4];
const numSet = new Set(nums);
console.log(numSet);//Set(5) {1, 2, 3, 4, 5}
//将Set转换为数组
const numArr=[...numSet];
console.log(numArr);//[1, 2, 3, 4, 5]
let Raymond={name:'Raymond',age:30};
let Fred={name:'Fred',age:30};
const people=new WeakSet([Raymond,Fred]);
people.add('Alex');//Uncaught TypeError: Invalid value used in weak set
let Raymond = { name: "Raymond", age: 30 };
let Fred = { name: "Fred", age: 30 };
const people = new WeakSet([Raymond, Fred]);
for (let person of people) {
console.log(person);
}
//Uncaught TypeError: people is not iterable
因为WeakSet没有配置iterator(迭代器/遍历器)
let Raymond = { name: "Raymond", age: 30 };
let Fred = { name: "Fred", age: 30 };
const people = new WeakSet([Raymond, Fred]);
people.forEach(item => console.log(item));
//Uncaught TypeError: people.forEach is not a function
let Raymond = { name: "Raymond", age: 30 };
let Fred = { name: "Fred", age: 30 };
// const people = new WeakSet([Raymond, Fred]);
const peopleArr=[Raymond,Fred];
console.log(peopleArr);
Fred=null;//把Fred对象删除掉
console.log(peopleArr);//Fred仍然存在,这是常说的内存泄漏
//由于peopleArr里还存在对Fred的引用,因此Fred没有被删除
而WeakSet不同,会帮我们把引用删除
let Raymond = { name: "Raymond", age: 30 };
let Fred = { name: "Fred", age: 30 };
let people = new WeakSet([Raymond, Fred]);
console.log(people);
Fred=null;//把Fred对象删除掉
console.log(people);//虽然此时Fred还在,但是在控制台中输入people再次查看,Fred没有了
简单用法:
set()
添加元素,size
获取键值对数量const people=new Map();
people.set('Raymond',20);//people.set(key,value)
people.set('Fred',30);
people.set('Greyson',21);
console.log(people.size);//3
//调用构造函数时初始化
const fruits=new Map([['apple',6],['banana',5]]);
console.log(fruits);
const people=new Map();
people.set({},3)
console.log(people);
get()
const people = new Map();
people.set("Raymond", 20);
people.set("Fred", 30);
people.set("Greyson", 21);
console.log(people.get("Raymond")); //20
delete()
删除元素,has()
查看某个属性是否存在,clear()
清楚所有元素const people = new Map();
people.set("Raymond", 20);
people.set("Fred", 30);
people.set("Greyson", 21);
people.delete('Raymond');
console.log(people.has("Raymond")); //false
console.log(people.has("Fred")); //true
people.clear();
console.log(people);//Map(0) {}
const people = new Map();
people.set("Raymond", 20);
people.set("Fred", 30);
people.set("Greyson", 21);
for (person of people) {
console.log(person);
}
//可以对person进解构
for (let [key,value] of people) {
console.log(key,value);
}
people.forEach(function(value, key, map) {
console.log(value, key, map);
});
简单应用
**方法二:**通过原数据来存储button相对应的信息
<button>Hellobutton>
<button>Hibutton>
<button>Worldbutton>
const clickCounts=new Map();
const buttons=document.querySelectorAll('button');
buttons.forEach(button=>{
clickCounts.set(button,0);
button.addEventListener('click',function(){
const val=clickCounts.get(this);
clickCounts.set(this,val+1);
console.log(clickCounts);
})
})
let Raymond = { name: "Raymond" };//对象作为key
let Fred = { name: "Fred" };
const strong = new Map();
const weak = new WeakMap();
strong.set(Raymond,'Hi,Raymond');
weak.set(Fred,'Hi,Fred');
console.log(strong);
console.log(weak);
console.log(strong.size);//1
console.log(weak.size)//undefined
let Raymond = { name: "Raymond" }; //对象作为key
let Fred = { name: "Fred" };
const strong = new Map();
const weak = new WeakMap();
strong.set(Raymond, "Hi,Raymond");
weak.set(Fred, "Hi,Fred");
weak.set("Gray", 20);//Uncaught TypeError: Invalid value used as weak map key
let Raymond = { name: "Raymond" };//对象作为key
let Fred = { name: "Fred" };
const strong = new Map();
const weak = new WeakMap();
strong.set(Raymond,'Hi,Raymond');
weak.set(Fred,'Hi,Fred');
for(let person of weak){
console.log(person);//Uncaught TypeError: weak is not iterable
}
因为WeakMap没有配置iterator(迭代器/遍历器)
let Raymond = { name: "Raymond" }; //对象作为key
let Fred = { name: "Fred" };
const strong = new Map();
const weak = new WeakMap();
strong.set(Raymond, "Hi,Raymond");
weak.set(Fred, "Hi,Fred");
Raymond = null;
Fred = null;
在控制台输入
console.log(strong);
console.log(weak);
可见strong里还有Raymond,weak里什么都没有