Promise
初识Promise
Promise是什么
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Promise 是什么</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all 0.5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 1.认识 Promise
// Promise 是异步操作的一种解决方案
// 回调函数
document.addEventListener(
'click',
() => {
console.log('这里是异步的');
},
false
);
console.log('这里是同步的');
// 2.什么时候使用 Promise
// Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
// 运动
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.addEventListener(
'transitionend',
() => {
// console.log('end');
end();
},
false
);
};
const boxEl = document.getElementById('box');
//回调层数太多【又称为回调地狱】,后期修改会非常麻烦。
document.addEventListener(
'click',
() => {
move(boxEl, { x: 150 }, () => {
move(boxEl, { x: 150, y: 150 }, () => {
move(boxEl, { y: 150 }, () => {
// console.log('object');
move(boxEl, { x: 0, y: 0 });
});
});
});
},
false
);
</script>
</body>
</html>
Promise的基本用法
实例化构造函数生成实例对象
Promise的状态
then方法
resolve和reject函数的参数
// 1.实例化构造函数生成实例对象
console.log(Promise); //构造函数 ƒ Promise() { [native code] }
// Promise 解决的不是回调函数,而是回调地狱
const p = new Promise(() => {}); //注意传递一个回调函数
console.log(p); //Promise {<pending>}
// 2.Promise 的状态
const p = new Promise((resolve, reject) => {
// Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功;执行 reject,变成 rejected,已失败
// Promise 的状态一旦变化,就不会再改变了
// pending->fulfilled
resolve();
// pending->rejected
reject();
});
// 3.then 方法 当Promise的状态变换时调用then函数
p.then(
() => {
console.log('success'); //p->fulfilled状态调用这里
},
() => {
console.log('error'); //p->rejected状态调用这里
}
);
// 4.resolve 和 reject 函数的参数
const p = new Promise((resolve, reject) => {
// Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
// 执行 reject,变成 rejected,已失败
// Promise 的状态一旦变化,就不会再改变了
// pending->fulfilled
// resolve('succ');
resolve({ username: 'alex' });
// pending->rejected
// reject('reason');
reject(new Error('reason'));
});
p.then(
data => {
console.log('success', data);
},
err => {
console.log('error', err);
}
);
console.log(p);
// Promise {<fulfilled>: {…}}
// success {username: 'alex'}
Promise的实例对象方法
then()
// 1.什么时候执行
// pending->fulfilled 时,执行 then 的第一个回调函数
// pending->rejected 时,执行 then 的第二个回调函数
// 2.执行后的返回值
// then 方法执行后返回一个新的 Promise 对象
const p = new Promise((resolve, reject) => {
resolve();
// reject();
});
const p2 = p
.then(
() => {},
() => {}
).then().then();
//注意这里的p和p2是不同的Promise()对象,p的状态也不影响p2的状态
console.log(p, p2, p === p2); //Promise {<fulfilled>: undefined} Promise {<pending>} false
// 3.then 方法返回的 Promise 对象的状态改变
const p = new Promise((resolve, reject) => {
// resolve();
reject();
});
p.then(
() => {
console.log('success1');
},
() => {
console.log('err1');
// 如果这里什么都不写默认返回undefined包装的Promoise对象,即这里的状态为fulfilled
// 在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
//return undefined;
// 等价于
// return new Promise(resolve => {
// resolve(undefined);
// });
return 123;
// 等价于
// return new Promise(resolve => {
// resolve(123);
// });
// 默认返回的永远都是成功状态的 Promise 对象,除非刻意调用reject()方法
// return new Promise((resolve, reject) => {
// reject('reason');
// });
}
)
.then(
data => {
console.log('success2', data);
// return undefined;
return new Promise(resolve => {
resolve(undefined);
});
},
err => {
console.log('err2', err);
}
)
.then(
data => {
console.log('success3', data);
},
err => {
console.log('err3', err);
}
);
使用Promise解决回调地狱
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>then()</title>
<style>
* {
padding: 0;
margin: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all 0.5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.addEventListener(
'transitionend',
() => {
end();
},
false
);
};
const boxEl = document.getElementById('box');
const movePromise = (el, point) => {
return new Promise(resolve => {
move(el, point, () => {
resolve();
});
});
};
document.addEventListener(
'click',
() => {
movePromise(boxEl, { x: 150 })
.then(() => {
return movePromise(boxEl, { x: 150, y: 150 });
})
.then(() => {
return movePromise(boxEl, { y: 150 });
})
.then(() => {
return movePromise(boxEl, { x: 0, y: 0 });
});
},
false
);
</script>
</body>
</html>
catch()
本质上是then(null,err=>{})的语义化
// 1.有什么用
// then(
// data => {},
// err => {}
// );
// then(data => {});
// catch 专门用来处理 rejected 状态
// catch 本质上是 then 的特例
// then(null, err => {}); //这里接收第2个参数
new Promise((resolve,reject) => {
// resolve('123')
reject("reason");
})
.then(data=>{console.log(data);}) //then方法里没有第2个参数无法捕捉rejected跳过
.catch(err => { //默认在这里捕捉rejected
console.log(err);
});
new Promise((resolve,reject) => {
reject("reason");
})
.then(data=>{console.log(data);},data=>{console.log(data);}) //then方法第2个函数捕捉到了rejected状态
new Promise((resolve,reject) => {
reject("reason");
})
.then(data=>{console.log(data);}) //then方法里没有第2个参数无法捕捉rejected跳过
.then(null,err => {console.log(err);}) //then方法里有第2个参数就在这里捕捉rejected
.catch(err=>{console.log(err+"catch");}) //上面捕捉到之后不会往下传递rejected,而是传递了fulfilled状态,如果上面没有捕获这里捕获后会返回fulfilled状态
.then(data=>{console.log("hello")}) //执行这里的函数
new Promise((resolve,reject) => {
reject("reason");
})
.then(data=>{console.log(data);}) //then方法里没有第2个参数无法捕捉rejected跳过
.catch(err=>{
console.log(err+"Catch"); //reasonCatch
//默认返回
//return new Promise(resolve => {
// resolve(undefined);
// });
throw new Error("throw new reason"); //Error: throw new reason
})
.then(data=>{console.log(data);})
.catch(err => {console.log(err);});
// catch() 可以捕获它前面的错误
// 一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误
finally()
finally无法接受参数,只是用来处理最终状态
本质上就是then()方法对于两种状态的处理
// 1.什么时候执行
// 当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.finally(data => {
console.log(data); //这里并不会打印,finally无法接受参数,只是用来处理最终状态
})
.catch(err => {console.log(err)});
// 2.本质
// finally() 本质上是 then() 的特例
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.finally(data => {
console.log(data);
})
.catch(err => {});
// 等同于
new Promise((resolve, reject) => {
// resolve(123);
reject('reason');
})
.then(
result => {
return result;
},
err => {
return new Promise((resolve, reject) => {
reject(err);
});
}
)
.then(data => {
console.log(data);
})
.catch(err => {
console.log(err);
});
Promise构造函数方法
Promise.resolve()
- 参数是 PromisePromise实例对象时,直接返回这个对象
- 参数是具有then方法的对象时,会立即执行它的then方法
- 参数是其他值时,相当于通过resolve函数传参
// 1.Promise.resolve()
// 是成功状态 Promise 的一种简写形式
new Promise(resolve => resolve('foo')); //Promise {<fulfilled>: 'foo'}
// 简写
Promise.resolve('foo'); //Promise.resolve('foo');
// 参数
// 一般参数
Promise.resolve('foo').then(data => {
console.log(data);
}); //foo
// Promise
// 当 Promise.resolve() 接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
// 相当于下面这个
// setTimeout(() => {
// resolve('我执行了');
// }, 1000);
});
Promise.resolve(p1).then(data => {
console.log(data);
});
// 等价于
p1.then(data => {
console.log(data); //
});
console.log(Promise.resolve(p1) === p1); //true
// 当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
new Promise(resolve => resolve(p1)).then(data => {
console.log(data); //我执行了 p1->then
});
// 具有 then 方法的对象
function func(obj) {
obj.then(1, 2);
}
func({
then(res, rej) {
console.log(res, rej);
}
}); // 1 2
const thenable = {
then(resolve, reject) { //这里的resolve和reject叫什么都行 关键要保持一致
console.log('then');
// resolve('data');
// reject('reason');
}
};
Promise.resolve(thenable).then(
data => console.log(data), //根据then返回的情况来决定执行哪个方法
err => console.log(err)
);
console.log(Promise.resolve(thenable));
Promise.reject()
- 失败状态 Promise 的一种简写形式
new Promise((resove,reject) => {
reject("reason");
});
//等价于
Promise.reject("reason");
- 不管什么参数,都会原封不动地向后传递,作为后续方法的参数
// 2.Promise.reject()
// 失败状态 Promise 的一种简写形式
new Promise((resolve, reject) => {
reject('reason');
});
// 等价于
Promise.reject('reason');
// 参数
// 不管什么参数,都会原封不动地向后传递,作为后续方法的参数
const p1 = new Promise(resolve => {
setTimeout(resolve, 1000, '我执行了');
});
Promise.reject(p1).catch(err => console.log(err)); //不会执行setTimeout方法,而是打印Promise {<pending>}对象
new Promise((resolve, rejcet) => {
resolve(123);
})
.then(data => {
// return data;
// return Promise.resolve(data);
return Promise.reject('reason');
})
.then(data => {
console.log(data);
})
.catch(err => console.log(err)); //reason
Promise构造函数方法Promise.all/race/allSettled
- 只要是可遍历的,都可作为参数
- 参数的“集合”中若有成员不是 Promise 对象,内部会将其转变成 Promise 对象
- 返回一个新的 Promise 实例对象
- 错误既可以单独处理,也可以统一处理
Promise.all()
- 所有状态都变成 resolved,最终的状态才会变成 resolved
- 只要有一个变成rejected,最终的状态就变成rejected
// 1.有什么用
// Promise.all() 关注多个 Promise 对象的状态变化
// 传入多个 Promise 实例,包装成一个新的 Promise 实例返回
// 2.基本用法
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1 完成了');
// return 'p1';
return Promise.reject('reason1');
});
const p2 = delay(2000).then(() => {
console.log('p2 完成了');
return 'p2';
// return Promise.reject('reason2');
});
// Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关
// 所有状态都变成 resolved,最终的状态才会变成 resolved
// 只要有一个变成 rejected,最终的状态就变成 rejected
const p = Promise.all([p1, p2]);
p.then(
data => {
console.log(data); //['p1','p2']
},
err => {
console.log(err); //p1被reject则打印reason1,p2被reject则打印reason2,都被rejected则只打印先被rejected的
}
);
Promise.race()
- 最终的状态取决于第一个完成的 Promise 实例对象
- 如果第一个完成的成功了,那最终的就成功
- 如果第一个完成的失败了,那最终的就失败
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(2000).then(() => {
console.log('p1 完成了');
// return 'p1';
return Promise.reject('reason1');
});
const p2 = delay(1000).then(() => {
console.log('p2 完成了');
return 'p2';
// return Promise.reject('reason2');
});
// 1.Promise.race()
// Promise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败,这里取决于谁先完成谁说了算和p1、p2的顺序无关
const racePromise = Promise.race([p1, p2]);
racePromise.then(
data => {
console.log(data);
},
err => {
console.log(err);
}
);
//p2 完成了
//p2 -> racePromise里的第一个函数执行结果
//p1完成了
Promise.allSettled()
- 最终的状态永远都是成功的,与传入的 Promise 对象状态无关
- 会记录下各个 Promise 的表现
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(2000).then(() => {
console.log('p1 完成了');
// return 'p1';
return Promise.reject('p1被rejectd了');
});
const p2 = delay(1000).then(() => {
console.log('p2 完成了');
return 'p2';
// return Promise.reject('p2被rejectd了');
});
// 2.Promise.allSettled()
// Promise.allSettled() 的状态与传入的Promise 状态无关
// 永远都是成功的
// 它只会忠实的记录下各个 Promise 的表现
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then(
data => {
console.log('succ', data);
},
err => {console.log("err",err);
}
);
// p2 完成了
// p1 完成了
//succ
//[{0: {status: 'rejected', reason: 'p1被rejectd了'}},{1: {status: 'fulfilled', value: 'p2'}}]
Promise的注意事项和应用
Promise的注意事项
// 1.resolve 或 reject 函数执行后的代码
// 推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
new Promise((resolve, reject) => {
// return resolve(123);
return reject('reason');
console.log('hi'); //无论是resolve还是reject这里都会执行
});
// 2.Promise.all/race/allSettled 的参数问题
// 参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象
Promise.all([1, 2, 3]).then(datas => {
console.log(datas);
});
// 等价于
Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]).then(datas => {
console.log(datas);
});
// 不只是数组,任何可遍历的都可以作为参数
// 数组、字符串、Set、Map、NodeList、arguments
Promise.all(new Set([1, 2,1, 3])).then(datas => {
console.log(datas);
});
// 3.Promise.all/race/allSettled 的错误处理
const delay = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log('p1 完成了');
// return 'p1';
return Promise.reject('reason1');
});
// .catch(err => {
// console.log('p1', err);
// });
const p2 = delay(2000).then(() => {
console.log('p2 完成了');
// return 'p2';
return Promise.reject('reason2');
});
// .catch(err => {
// console.log('p2', err);
// });
const allPromise = Promise.all([p1, p2]);
allPromise
.then(datas => {
console.log(datas);
})
.catch(err => console.log(err));
// 错误既可以单独处理,也可以统一处理
// 一旦被处理,就不会在其他地方再处理一遍
Promise的应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Promise 的应用</title>
<style>
#img {
width: 80%;
padding: 10%;
}
</style>
</head>
<body>
<img
src="https://img.mukewang.com/5e6af63d00011da318720764.jpg"
alt=""
id="img"
/>
<script>
// 异步加载图片
const loadImgAsync = url => {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error(`Could not load image at ${url}`));
};
img.src = url;
});
};
const imgDOM = document.getElementById('img');
loadImgAsync('https://img.mukewang.com/5f057a6a0001f4f918720764.jpg')
.then(img => {
console.log(img.src);
setTimeout(() => {
imgDOM.src = img.src;
}, 1000);
})
.catch(err => {
console.log(err);
});
</script>
</body>
</html>
Class
初识Class
Class是什么
// 1.认识 Class
// 人类:类
// 具体的人:实例、对象
// 类可以看做是对象的模板,用一个类可以创建出许多不同的对象
// 2.Class 的基本用法
// 类名一般大写
// class Person {} √
// class Person() {} ×
// class Person {}; ×
// function func() {}
class Person {
// 实例化时执行构造方法,所以必须有构造方法,但可以不写出来
constructor(name, age) {
// console.log('实例化时执行构造方法');
// this 代表实例对象,上面定义的是实例属性/方法
this.name = name;
this.age = age;
// 一般在构造方法中定义属性,方法不在构造方法中定义
// this.speak = () => {};
}
// speak:function(){}
// 各实例共享的方法
speak() {
console.log('speak');
}
}
// Person();
const zs = new Person('ZS', 18);
const ls = new Person('LS', 28);
console.log(zs.name);
console.log(zs.age);
console.log(zs.speak);
zs.speak();
console.log(ls.name);
console.log(ls.age);
console.log(ls.speak);
console.log(zs.speak === ls.speak); //如果方法定义在构造函数中则不相等,定义在构造函数外部则是相等的。
// 3.Class 与构造函数
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
// this.speak = () => {};
}
speak() {
console.log('speak');
}
run(){console.log("run");} //推荐在这里写方法,而不是在外部的原型对象上添加方法
}
console.log(typeof Person); //function
// Person.prototype.run = function () {}; //不推荐在这里写方法
console.log(Person.prototype.speak);
//本质上等价于下面这种写法
function Person(name, age) {
this.name = name;
this.age = age;
// this.speak = () => {};
}
Person.prototype.speak = function () {};
Class的两种声明形式
- 声明形式
- class Person{}
- 表达式形式
- const Person = class{};
- new (class{})();
// 1.声明形式
class Person {
constructor() {}
speak() {}
}
// 2.表达式形式
function Person(){}
const Person = function () {};
const Person = class {
constructor() {
console.log('constructor');
}
speak() {}
};
//实例化对象
new Person();
//立即执行的匿名函数
(function () {
console.log('func');
})();
//立即执行的匿名类
new (class {
constructor() {
console.log('constructor');
}
})();
new Person(); //上面class外部的括号部就像等于这里的Person
Class的属性和方法
实例属性、静态方法和静态属性
// 1.实例属性
// 方法就是值为函数的特殊属性
class Person {
age = 0;
sex = 'male';
getSex = function () {
return this.sex;
};
constructor(name, sex) {
this.name = name;
// this.age = 18;
this.sex = sex;
}
// speak() {
// this.age = 18;
// }
}
const p = new Person('Alex');
console.log(p.name); //Alex
console.log(p.age); //0
// 2.静态方法
// 类的方法
class Person {
constructor(name) {
this.name = name;
}
//实例对象方法
speak() {
console.log('speak');
console.log(this); //指向this对象
}
// 静态方法
static speak() {
console.log('人类可以说话');
// this 指向类
console.log(this);
}
}
// 可以在外部为类添加属性 但不符合封装性原则
// Person.speak = function () {
// console.log('人类可以说话');
// console.log(this);
// };
const p = new Person('Alex');
p.speak();
Person.speak();
// 3.静态属性
// 类的属性
class Person {
constructor(name) {
this.name = name;
}
// 不要这么写,目前只是提案,有兼容性问题
// static version = '1.0';
static getVersion() {
return '1.0';
}
}
// 在外部添加,但有违背封装性原则
// Person.version = '1.0';
const p = new Person('Alex');
console.log(p.name);
// console.log(Person.version);
console.log(Person.getVersion());
私有属性和方法
- _ 开头表示私有
- 将私有属性和方法移出类
// 1.为什么需要私有属性和方法
// 一般情况下,类的属性和方法都是公开的
// 公有的属性和方法可以被外界修改,造成意想不到的错误
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log('speak');
}
getName() {
return this.name;
}
}
const p = new Person('Alex');
console.log(p.name); //不要通过这种方式来访问,而是通过get方法
p.speak();
// ....
p.name = 'zs';
console.log(p.name);
// 2.模拟私有属性和方法
// 2.1 _开头表示私有,但是没有很强的约束力
class Person {
constructor(name) {
this._name = name;
}
speak() {
console.log('speak');
}
getName() {
return this._name;
}
}
const p = new Person('Alex');
// console.log(p.name);
p.name = 'zd';
console.log(p.getName());
// 2.2.将私有属性和方法移出类 有较强的约束力
// 立即执行函数可以模拟一个模块
(function () {
let name = '';
class Person {
constructor(username) {
// this.name = name;
name = username;
}
speak() {
console.log('speak');
}
getName() {
return name;
}
}
window.Person = Person;
})();
(function () {
const p = new Person('Alex');
console.log(p.name); //undefined;
console.log(p.getName()); //Alex
})();
Class的继承
extends
- 使用extends可以实现继承
- 可以改写继承到的属性或方法,同名覆盖
// 1.子类继承父类
class Person {
constructor(name, sex) {
this.name = name;
this.sex = sex;
this.say = function () {
console.log('say');
};
}
speak() {
console.log('speak');
}
static speak() {
console.log('static speak');
}
}
Person.version = '1.0';
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
}
}
const zs = new Programmer('zs', '男');
console.log(zs.name); //zs
console.log(zs.sex); //男
zs.say(); //say
zs.speak(); //speak
Programmer.speak(); //static speak
console.log(Programmer.version); //1.0
// 2.改写继承的属性或方法
class Programmer extends Person {
constructor(name, sex, feature) {
// this.feature = feature; ×
// this 操作不能放在 super 前面
super(name, sex);
this.feature = feature;
}
hi() {
console.log('hi');
}
// 同名覆盖
speak() {
console.log('Programmer speak');
}
static speak() {
console.log('Programmer static speak');
}
}
Programmer.version = '2.0';
const zs = new Programmer('zs', '男', '秃头');
console.log(zs.name);
console.log(zs.sex);
console.log(zs.feature);
zs.say();
zs.speak();
zs.hi();
Programmer.speak();
console.log(Programmer.version);
Super
- 作为函数调用
- super 代表父类的构造方法,只能用在子类的构造方法中
- 内部的this指向子类的实例
- 作为对象使用
- 在构造方法和一般方法中使用
- super 代表父类的原型对象
- 通过super调用父类的方法时,方法的指向当前的this子类实例
- 在静态方法中使用
- super代表父类
- 通过super调用父类的方法时,方法的this指向当前的子类
- 在构造方法和一般方法中使用
- 使用super的时候,必须显式指定作为函数还是作为对象使用
// 1.作为函数调用
// 代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错
// super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
}
// hi() {
// super(); // ×
// }
}
new Person();
new Programmer();
// 2.作为对象使用
// 2.1.在构造方法中使用或一般方法中使用
// super 代表父类的原型对象 Person.prototype
// 所以定义在父类实例上的方法或属性,是无法通过 super 调用的
// 通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例
class Person {
constructor(name) {
this.name = name;
console.log(this);
}
speak() {
console.log('speak');
console.log(this);
}
static speak() {
console.log('Person speak');
console.log(this);
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
// console.log(super.name); //undefined 无法获取父类实例对象的属性,因为这个东西不在原型链上 而是在是实例对象中
// super.speak(); //访问父类的方法,但此时this指向的依然是子类实例对象
}
// hi() {
// super(); // ×
// }
speak() {
super.speak();
console.log('Programmer speak');
}
//2.2.在静态方法中使用
// 指向父类,而不是父类的原型对象
// 通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
static speak() {
super.speak();
console.log('Programmer speak');
}
}
// new Person();
// new Programmer();
Programmer.speak();
// 3.注意事项
// 使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log('speak');
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex);
// console.log(super); // ×
// console.log(super()); //加入()当作函数
// console.log(super.speak); //加入.当作对象
}
}
Class的应用
slider.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Class 的应用</title>
<link rel="stylesheet" href="./slider.css" />
</head>
<body>
<div class="slider-layout">
<div class="slider">
<div class="slider-content">
<div class="slider-item">
<a href="javascript:;"
><img src="https://img-blog.csdnimg.cn/b8dca691b87f438c94cb17ad909997c2.jpg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="https://img-blog.csdnimg.cn/5a5f93c5464149a4b11f09d684129cbd.jpg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="https://img-blog.csdnimg.cn/e81d8eba458948668b30c465c0c907c0.jpg" alt="1" class="slider-img"
/></a>
</div>
<div class="slider-item">
<a href="javascript:;"
><img src="https://img-blog.csdnimg.cn/06c0a7c463a34b93a9ce7a49f9cf08bb.jpg" alt="1" class="slider-img"
/></a>
</div>
</div>
</div>
</div>
<script src="./base.js"></script>
<script>
// console.log(BaseSlider);
class Slider extends BaseSlider {
constructor(el, options) {
super(el, options);
this._bindEvent();
}
_bindEvent() {
document.addEventListener('keyup', ev => {
// console.log(ev.keyCode);
if (ev.keyCode === 37) {
// ←
this.prev();
// super.prev(); //也可以
} else if (ev.keyCode === 39) {
// →
this.next();
// super.next(); //也可以
}
});
}
}
new Slider(document.querySelector('.slider'), {
initialIndex: 1,
animation: true,
speed: 1000
});
</script>
</body>
</html>
base.js
// 默认参数
const DEFAULTS = {
// 初始索引
initialIndex: 0,
// 切换时是否有动画
animation: true,
// 切换速度,单位 ms
speed: 300
};
// base
const ELEMENT_NODE = 1;
const SLIDER_ANIMATION_CLASSNAME = 'slider-animation';
class BaseSlider {
constructor(el, options) {
console.log(options)
if (el.nodeType !== ELEMENT_NODE)
throw new Error('实例化的时候,请传入 DOM 元素!');
// 实际参数
this.options = {
...DEFAULTS,
...options
};
const slider = el;
const sliderContent = slider.querySelector('.slider-content');
const sliderItems = sliderContent.querySelectorAll('.slider-item');
// 添加到 this 上,为了在方法中使用
this.slider = slider;
this.sliderContent = sliderContent;
this.sliderItems = sliderItems;
this.minIndex = 0;
this.maxIndex = sliderItems.length - 1;
this.currIndex = this.getCorrectedIndex(this.options.initialIndex);
// 每个 slider-item 的宽度(每次移动的距离)
this.itemWidth = sliderItems[0].offsetWidth;
this.init();
}
// 获取修正后的索引值
// 随心所欲,不逾矩
getCorrectedIndex(index) {
if (index < this.minIndex) return this.maxIndex;
if (index > this.maxIndex) return this.minIndex;
return index;
}
// 初始化
init() {
// 为每个 slider-item 设置宽度
this.setItemsWidth();
// 为 slider-content 设置宽度
this.setContentWidth();
// 切换到初始索引 initialIndex
this.move(this.getDistance());
// 开启动画
if (this.options.animation) {
this.openAnimation();
}
}
// 为每个 slider-item 设置宽度
setItemsWidth() {
for (const item of this.sliderItems) {
item.style.width = `${this.itemWidth}px`;
}
}
// 为 slider-content 设置宽度
setContentWidth() {
this.sliderContent.style.width = `${
this.itemWidth * this.sliderItems.length
}px`;
}
// 不带动画的移动
move(distance) {
this.sliderContent.style.transform = `translate3d(${distance}px, 0px, 0px)`;
}
// 带动画的移动
moveWithAnimation(distance) {
this.setAnimationSpeed(this.options.speed);
this.move(distance);
}
// 设置切换动画速度
setAnimationSpeed(speed) {
this.sliderContent.style.transitionDuration = `${speed}ms`;
}
// 获取要移动的距离
getDistance(index = this.currIndex) {
return -this.itemWidth * index;
}
// 开启动画
openAnimation() {
this.sliderContent.classList.add(SLIDER_ANIMATION_CLASSNAME);
}
// 关闭动画
closeAnimation() {
this.setAnimationSpeed(0);
}
// 切换到 index 索引对应的幻灯片
to(index) {
index = this.getCorrectedIndex(index);
if (this.currIndex === index) return;
this.currIndex = index;
const distance = this.getDistance();
if (this.options.animation) {
return this.moveWithAnimation(distance);
} else {
return this.move(distance);
}
}
// 切换上一张
prev() {
this.to(this.currIndex - 1);
}
// 切换下一张
next() {
this.to(this.currIndex + 1);
}
// 获取当前索引
getCurrIndex() {
return this.currIndex;
}
}
slider.css
/* css reset */
* {
padding: 0;
margin: 0;
}
a {
text-decoration: none;
outline: none;
}
img {
vertical-align: top;
}
/* layout */
.slider-layout {
width: 80%;
height: 420px;
margin: 0 auto;
}
/* slider */
.slider,
.slider-content,
.slider-item,
.slider-img {
width: 100%;
height: 100%;
}
.slider {
overflow: hidden;
}
.slider-item {
float: left;
}
.slider-animation {
transition-property: transform;
transition-duration: 0ms;
}