
十一、类和继承
1、版本
ES5以前的:
function Person(){
this.name = 'aaa';
}
Person.prototype.showName = function(){};
ES6中变形:
(形式1)
class Person{
constructor(){
this.name = 'aaa';
} //这里不能加逗号
showName(){}
}
(形式2)
const Person = class{
constructor(){
this.name = 'aaa';
} //这里不能加逗号
showName(){}
}
注意:constructor和方法之间不要随意加逗号
// 以前的
// 方法1
function Person(name,age){
this.name = name;
this.age = age;
}
Person.prototype.showName = function(){
return `名字为${this.name}`;
};
Person.prototype.showAge = function(){
return `年龄为${this.age}`;
};
let p1 = new Person('张三',18);
//输出为 名字为张三 年龄为18
console.log(p1.showName(),p1.showAge());
// 方法2
function Person2(name2,age2){
this.name2 = name2;
this.age2 = age2;
}
Object.assign(Person2.prototype,{
showName(){
return `名字为${this.name2}`;
},
showAge(){
return `年龄为${this.age2}`;
}
});
let p2 = new Person2('李四',20);
//输出为 名字为李四 年龄为20
console.log(p2.showName(),p2.showAge());
// 现在的
class Person {
constructor(name, age) {
//构造方法(函数),调用new,自动执行
this.name = name;
this.age = age;
}
showName() {
return `名字为${this.name}`;
}
showAge() {
return `年龄为${this.age}`;
}
}
let p1 = new Person('张三', 18);
// 输出为 名字为张三 年龄为18
console.log(p1.showName(),p1.showAge());
2、注意事项
注意:1、ES6 类中属性名可以用表达式
let aaa = 'a';
let bbb = 'b'
class Person {
[aaa+bbb](){
return '随便啦》》》';
}
}
let p1 = new Person();
console.log(p1[aaa+bbb]()); // 输出 随便啦》》》
2、ES6 里面 class 没有提升功能,在ES5,用函数模拟可以,默认函数提升
3、ES6 里面this指向 比以前轻松多了
矫正this:
a)fn.call(this指向谁,args1,args2......)
b)fn.apply(this指向谁,[args1,args2.......])
c)fn.bind()
class Person{
constructor(){
this.name = 'strive';
// 因为下面加了let {showName} = p1;,this将指向不明确,所以需要矫正this
this.showName = this.showName.bind(this);//矫正this
}
showName(){
console.log('this',this);
return `名字为${this.name}`;
}
}
let p1 = new Person();
let {showName} = p1; //这句话纯属没事找事,把this指向弄乱了
console.log(showName());
3、class
(1)getter 和 setter
class Person{
constructor(){
}
set aaa(val){
console.log(`设置aaa属性,值为:${val}`);
}
get aaa(){
return `aaa的属性`;
}
}
let p1 = new Person();
p1.aaa = '123';
console.log(p1.aaa); //输出 aaa的属性
(2)静态方法static
(3)继承
//以前的
// 父类
function Person(name){
this.name = name;
}
Person.prototype.showName = function(){
return `名字为${this.name}`;
}
// 子类
function Student(name,skill){
Person.call(this,name); //继承属性
this.skill = skill;
}
Student.prototype = new Person(); //继承方法
//调用
let stu1 = new Student('strive','逃学');
console.log(stu1.showName()); //输出为 名字为strive
//现在的
// 父类
class Person{
constructor(name){
this.name = name;
}
showName(){
console.log('父级的showName,'+`名字为${this.name}`);//输出 父级的showName,名字为strive
return `名字为${this.name}`;
}
}
// 子类
class Student extends Person{
constructor(name,skill){
super(name);
this.skill = skill;
}
showSkill(){
return `哥们的技能为:${this.skill}`;
}
showName(){//这样做会覆盖掉父集的方法,要想父集方法也被调用,用下面这句
super.showName();
return `子类的showName,名字为${this.name}`;
}
}
//调用
let stu1 = new Student('strive','逃学');
console.log(stu1.showName()); //输出为 子类的showName,名字为strive
console.log(stu1.showSkill()); //输出为 哥们的技能为:逃学
(4)继承案例(拖拽div)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box{
width: 100px;
height: 100px;
background:red;
position: absolute;
top: 0;
}
.left{
left: 0;
}
.right{
right: 0;
}
</style>
</head>
<body>
<div id="div1" class="box left"></div>
<div id="div2" class="box right"></div>
<script>
// 普通的拖拽,父类
class Drug{
constructor(id){
this.oDiv = document.querySelector(id);
this.disX = 0;
this.disY = 0;
this.init();
}
init(){
this.oDiv.onmousedown = function(ev){
this.disX = ev.clientX - this.oDiv.offsetLeft;
this.disY = ev.clientY - this.oDiv.offsetTop;
document.onmousemove = this.fnMove.bind(this);
document.onmouseup = this.fnUp.bind(this);
return false;
}.bind(this);
}
fnMove(ev){
this.oDiv.style.left = ev.clientX - this.disX +'px';
this.oDiv.style.top = ev.clientY - this.disY +'px';
}
fnUp(){
document.onmousemove = null;
document.onmouseup = null;
}
}
//子类,限制范围
class limitDrug extends Drug{
fnMove(ev){
super.fnMove(ev); //继承父类的方法
// 这里做自己的事情,限制范围
if(this.oDiv.offsetLeft<=0){
this.oDiv.style.left = 0;
}
}
}
//调用
new Drug('#div1');
new limitDrug('#div2');
</script>
</body>
</html>
十二、Symbol & generator
1、Symbol
之前 typeof 出来的数据类型有 : number、string、undefined、function、boolean
Object
Symbol 使用情况一般,typeof 出来的是 symbol
定义:let syml = Symbol('aaa');
注意:(1) Symbol 不能 new
(2)Symbol 返回是一个唯一值,坊间传说,定义一些唯一或者私有一些东西
(3)Symbol 是一个单独的数据类型,就叫symbol
(4)如果用 symbol 作为key,用for in 循环,出不来
// 当作 key
let syml = Symbol('Strive');
let json = {
a:'apple',
b:'banana',
[syml]:'aaa'
}
// 当作key
console.log(json[syml]); // 输出 aaa
// 如果用 symbol 作为key,用for in 循环,出不来
let syml = Symbol('Strive');
let json = {
a:'apple',
b:'banana',
[syml]:'aaa'
}
for(let key in json){
console.log(key); // 输出 a b ,最后一个出不来
}
2、generator
解决异步深度嵌套的问题
(1)语法
generator函数,注意 * 左右有无空格无所谓
function * show(){
yield
}
定义和调用如下例:
// 定义
function * gen(){
yield 'welcome';
yield 'to';
yield 'mmr';
}
// 调用
let g1 = gen();
console.log(g1.next()); // 输出 {value: "welcome", done: false}
console.log(g1.next()); // 输出 {value: "to", done: false}
console.log(g1.next()); // 输出 {value: "mmr", done: false}
console.log(g1.next()); // 输出 {value: undefined, done: true}
(2)配合使用
配合使用的有:for...of、解构、扩展运算符、Array.from、axios
上述调用,手动调用,麻烦,下面用了for...of自动遍历generator,但是注意的是return的东西,它不会遍历
// for...of
// 定义
function * gen(){
yield 'welcome';
yield 'to';
yield 'mmr';
return '测试return'
}
// 调用
let g1 = gen();
for(let val of g1){
console.log(val) // 输出 welcome to mmr ,return的东西,它不会遍历
}
// 解构、扩展运算符、Array.from
// 定义
function * gen(){
yield 'welcome';
yield 'to';
yield 'mmr';
return '测试return'
}
// 调用
// 解构 1
let [a,b,c] = gen();
console.log(a,b,c); // 输出 welcome to mmr
// 解构 2
let [d,...e] = gen();
console.log(d,e); // 输出 welcome ["to", "mmr"]
//扩展运算符
console.log(...gen());// 输出 welcome to mmr
//Array.from
console.log(Array.from(gen())); // 输出 ["welcome", "to", "mmr"]
// generator 结合 axios 数据请求
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
// generator 结合 axios 数据请求
function * gen(){
let val = yield 'piterzhuhui';
yield axios.get(`https://api.github.com/users/${val}`);
}
let g1 = gen();
let username = g1.next().value;
console.log(username);
g1.next(username).value.then(res=>{
console.log(res.data); //这里返回的是自己github信息
})
</script>
补充知识:
异步:不连续,上一个操作没有执行完,下一个操作照样开始
同步:连续执行,上一个操作没有执行完,下一个操作没法开始。
关于异步,解决方案:
(a)回调函数
(b)事件监听
(c)发布/订阅
(d)Promise对象
十三、async、await
1、async、await
ES2017 ,规定 async ,async 告诉 程序这是一个异步程序,await 等待,配套使用
async function fn(){ //表示异步,这个函数里面有异步任务
let result = await XXX // 表示后面结果需要等待
}
例子:读取文件: fs.readFile (nodeJs)
(1)promise
(2)generator
(3)async
(1)promise
const fs = require('fs');
// 简单封装,fs 封装成一个promise
const readFile = function(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
// promise
readFile('data/a.txt').then(res=>{
console.log(res.toString());
// 这里可以返回数据,然后继续调用
return readFile('data/b.txt');
}).then(res=>{
console.log(res.toString());
// 这里可以返回数据,然后继续调用
return readFile('data/c.txt');
}).then(res=>{
console.log(res.toString());
})
从文件中读出来的:

(2)generator
const fs = require('fs');
// 简单封装,fs 封装成一个promise
const readFile = function(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
//generator
function * gen(){
yield readFile('data/a.txt');
yield readFile('data/b.txt');
yield readFile('data/c.txt');
}
let g1 = gen();
g1.next().value.then(res=>{
console.log(res.toString());
return g1.next().value;
}).then(res=>{
console.log(res.toString());
return g1.next().value;
}).then(res=>{
console.log(res.toString());
})
执行和上面一致,也是在终端用 node 命令:node .\generator.js
(3)async
const fs = require('fs');
// 简单封装,fs 封装成一个promise
const readFile = function(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
//async
// 定义
async function fn(){
let f1 = await readFile('data/a.txt');//返回的值就是文件的内容
console.log(f1.toString());
let f2 = await readFile('data/b.txt');
console.log(f2.toString());
let f3 = await readFile('data/c.txt');
console.log(f3.toString());
}
//调用
fn();
执行和上面一致,也是在终端用 node 命令:node .\async.js
(4)async特点
(a) await 只能放到 async 函数中
(b) 相比 generator 语义性更强
(c) await 后面可以是一个promise对象,也可以是数字,字符串,布尔。。。。(它会转成promise对象)
(d) async 函数返回的是一个 promise 对象
(e) 只要 await语句后面的 promise 状态变成reject,那么整个async 函数会中断执行
// async 函数返回的是一个 promise 对象
async function fn(){
return 'Welcome'
}
console.log(fn()); //输出 Promise {<resolved>: "Welcome"}
fn().then(res=>{
console.log(res); // 输出 Welcome
})
// 只要 await语句后面的 promise 状态变成reject,那么整个async 函数会中断执行
async function fn4(){
let a = await Promise.resolve('成功');
console.log(a); // 输出 成功
await Promise.reject('出现问题了');
let b = await Promise.resolve('成功2');// 注意这里不会执行,因为上一句有reject,函数就被中断执行了
console.log(b);
}
fn4().then(res=>{
console.log(res);
}).catch(err=>{
console.log(err); // 输出 出现问题了
})
注意:如何解决async 函数中抛出错误,影响后续代码,让它不影响
第一种:用 try——catch
try{
}catch(e){
}
第二种:用promise本身的catch
async function fn4(){
await Promise.reject('出现问题了').catch(err=>{
console.log(err); // 输出 出现问题了
});
let b = await Promise.resolve('成功2');
console.log(b); // 输出 成功2
}
fn4().then(res=>{
console.log(res);
}).catch(err=>{
console.log(err);
})
注意:建议所有的 await try_catch 把之前读文件例子改一下
//async
// 定义
async function fn(){
try{
let f1 = await readFile('data/a.txt');
let f2 = await readFile('data/b.txt');
let f3 = await readFile('data/c.txt');
}catch(e){
}
}
//调用
fn();
注意:当 await 的东西之间相互不影响时,可以用 Promise.all([]) 来做处理。
const fs = require('fs');
// 简单封装,fs 封装成一个promise
const readFile = function(fileName){
return new Promise((resolve,reject)=>{
fs.readFile(fileName,(err,data)=>{
if(err) reject(err);
resolve(data);
});
});
}
//async
// 定义
// 当三个过程没关联的时候可以这样用
async function fn(){
let [a,b,c] = await Promise.all([
readFile('data/a.txt'),
readFile('data/b.txt'),
readFile('data/c.txt')
]);
console.log(a.toString()); //输出 aaaa
console.log(b.toString()); //输出 bbbb
console.log(c.toString()); //输出 cccc
}
//调用
fn();
十四、set和WeakSet
1、set
概念:类似数组,但是里面不能有重复值
set用法 : 定义: new Set(['a','b'])
添加: arr.add(元素)
删除: arr.delete(元素)
判断: arr.has(元素)
大小:arr.size //返回个数
循环:for...of arr //默认是values值
for...of arr.keys()
for...of arr.values()
for...of arr.entries()
arr.forEach
类似链式操作:let setArr = new Set().add('a').add('b').add('c');
数组去重:把数组变成set,再由set数据结构变成数组
[...set]
set里面用map :转成数组,map处理之后再转成set
eg: set3 = new Set([...set3].map(val=>val*2));
set里面用filter:转成数组,filter处理之后再转成set
eg: set4 = new Set([...set4].filter(val=>val%2==0));
注意:set和WeakSet的区别是,set([]),WeakSet({})
set里面可以放对象,但是大多数不用,用WeakSet
确认:初始往里面添加对象,是不行的,最好用 add
// 定义
let arr = ['a','b','c','a'];
console.log(arr); //输出 (4) ["a", "b", "c", "a"]
let setArray = new Set(['a','b','c','a','b']);
console.log(setArray); //输出 Set(3) {"a", "b", "c"}
// 添加
let setArr = new Set();
setArr.add('a');
setArr.add('b');
console.log(setArr); // 输出 Set(2) {"a", "b"}
// 删除
setArr.delete('b');
console.log(setArr); // 输出 Set(1) {"a"}
// 判断元素是否存在
console.log(setArr.has('a')); // 输出 true
console.log(setArr.has('b')); // 输出 false
// set大小
console.log(setArr.size); // 输出 1
// 清除所有
setArr.clear();
console.log(setArr.size); // 输出 0
//循环
let setArr = new Set(['a','b','c']);
// 循环 默认
for(let item of setArr){
console.log(item); // 循环输出 a b c
}
//for...of arr.keys()
for(let item of setArr.keys()){
console.log(item); // 循环输出 a b c
}
//for...of arr.values()
for(let item of setArr.values()){
console.log(item); // 循环输出 a b c
}
//for...of arr.entries()
for(let item of setArr.entries()){
console.log(item); // 循环输出 ["a", "a"] ["b", "b"] ["c", "c"]
}
for(let [k,v] of setArr.entries()){
console.log(k,v); // 循环输出 a a b b c c
}
//arr.forEach (建议使用)
setArr.forEach((v,i)=>{
console.log(v,i); // 循环输出 a a b b c c
})
// set用map
// 以前
let set = new Set([1,2,3]); //注意括号里面是[]
let set2 = new Set();
for(let item of set.values()){
set2.add(item*2);
}
console.log(set2); // 输出 Set(3) {2, 4, 6}
let set3 = new Set([1,2,3]);
set3 = new Set([...set3].map(val=>val*2));
console.log(set3); // 输出 Set(3) {2, 4, 6}
// set 用filter
let set4 = new Set([1,2,3]);
set4 = new Set([...set4].filter(val=>val%2==0));
console.log(set4); // 输出 Set(1) {2}
// set() 里面存对象,但是注意不能直接往()里面放对象
let set = new Set();
let json = {
a:1,
b:2
}
let json2 = {
a:'apple',
b:2
}
set.add(json);
set.add(json2);
set.forEach((item)=>{
console.log(item.a); // 输出 1 apple
})
console.log(set.size); // 输出 2
2、WeakSet()
存储 json ,这种写法不靠谱,WeakSet没有size,有没有clear
但是有 add(),has(),delete()
let wSet = new WeakSet();
let json = {
a: 1,
b: 2
}
let json2 = {
a: 'apple',
b: 2
}
wSet.add(json);
wSet.add(json2);
console.log(wSet); //输出 WeakSet {{…}}
console.log(wSet.size);// 输出 undefined
总结:以后用 set。
网友评论