Promise
Promise 是什么
//1.认识 Promise: 是异步操作的一种解决方案
//回调函数
document.addEventListener(
"click",
() => {
console.log("这里是异步的");
},
false
);
console.log("这里是同步的");
//2.什么时候使用 Promise : 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题
//运动 box 移动右下左上
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},()=>{
move(boxEl,{x:0,y:0})
})
});
})
},false);
Promise 的基本用法[重点]
//1.实例化构造函数生成实例对象
//Promise 解决的不是回调函数,而是回调地狱
//2.Promise 的状态
const p = new Promise((resolve, reject) => {
//[重要]Promise 有 3 种状态,一开始是 pending(未完成),执行 resolve,变成 fulfilled(resolved),已成功
//执行 reject,变成 rejected,已失败
//Promise 的状态一旦变化,就不会再改变了
resolve();//success
});
console.log(p);
//3.then 方法
p.then(()=>{
console.log('success');
},()=>{
console.log('error');
})
//4.resolve 和 reject 函数的参数
const p1 = new Promise((resolve, reject) => {
// resolve({username:'alex'});
// reject('reason');
reject(new Error('reason'))
})
p1.then(
(data)=>{
console.log('success',data);
},(err)=>{
console.log('error',err);
}
)
then()[重点]
//1.什么时候执行
//pending->fulfilled 时,执行 then 的第一个回调函数
//pending->rejected 时,执行 then 的第二个回调函数
//2.执行后的返回值
//then 方法执行后返回一个新的 Promise 对象
const p = new Promise((resolve, reject) => {
resolve();
})
const p2= p.then(
() => {},//成功
() => {}
).then()
console.log(p,p2,p===p2);
//3.then 方法返回的 Promise 对象的状态改变
const p1 = new Promise((resolve, reject) => {
//resolve();//success success2
reject();//error success2
})
p1.then(//执行 success 这个回调
() => {
//console.log('success')
},
() => {
console.log('error')
//在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
return undefined;
//等价于
// return new Promise(resolve=>{
// resolve( undefined)
// })
//默认返回的永远都是成功状态的 Promise 对象,失败则写完整(resolve, reject)调用 reject
}
).then(//上边那个 then 的返回值决定后面的 then 执行回调
data => {
console.log('success2',data)
},
err => {
console.log('error2',err)
}
).then(
data => {
console.log('success3',data)
},
err => {
console.log('error3',err)
}
)
then()-2
//4.使用 Promise 解决回调地狱
const boxEI = document.getElementById("box");
//(1)纵向发展的,由外往内一层层嵌套的 [详细代码见标题 Promise 是什么]
const movePromise = (el, point) => {
return new Promise((resolve) => {
move(el, point, () => {
resolve();
});
});
};
//(2)横向发展的
document.addEventListener(
"click",
() => {
movePromise(boxEI, { x: 150 })
.then(() => {
return movePromise(boxEI, { x: 150, y: 150 });
})
.then(() => {
return movePromise(boxEI, { y: 150 });
})
.then(() => {
return movePromise(boxEI, { x: 0, y: 0 });
});
},
false
);
catch()[标红]
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 300px;
height: 300px;
background-color: red;
transition: all 0.5s;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
//then(data=>{});用的多
//catch 专门用来处理 rejected 状态,本质上是 then 的特例
//2.基本用法
new Promise((resolve, reject) => {
// resolve(123);
reject("reason");
})
.then((data) => {
console.log(data);
})
// .then(null,err=>{
// console.log(err);
// });等价于↓
.catch((err) => {
console.log(err);
//return undefined;
throw new Error("reason");
})
.then((data) => {
console.log(data); //undefined
})
.catch((err) => {
console.log(err);
});
//catch()可以捕获它前面的错误
//一般总是建议,Promise 对象后面要跟 catch 方法,
// 这样可以处理 Promise 内部发生的错误
finally() 了解,工作不常用
//1.什么时候执行
//当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行
new Promise((resolve, reject) => {
// resolve(123);undefined
reject("reason"); //undefined
})
.finally((data) => {
console.log(data);
})
.catch((err) => {});
//2.本质
//finally()本质上是 then()的特例
//上面等同于
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() 和 Promise.reject()
//1.Promise.resolve(): 是成功状态 Promise 的一种简写形式
new Promise((resolve) => resolve("foo"));
//简写: Promise.resolve('foo');
//参数
//一般参数 重点关注
Promise.resolve("foo").then((data) => {
console.log(data); //foo
});
/*了解就行,不用深究
//特殊参数,Promise 对象
// 当 Promise.resolve()接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做
const p = new Promise((resolve) => {
setTimeout(resolve, 1000, "我执行了");
//相当于
// setTimeout(()=>{
// resolve('我执行了')
// },1000)
});
Promise.resolve(p).then((data) => {
console.log(data); //我执行了
});
//等价于
p.then((data) => {
console.log(data);
});
console.log(Promise.resolve(p) === p1); //true
//当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
new Promise((resolve) => resolve(p)).then((data) => {
console.log(p);
});
//(2)具有 then 方法的对象,作为参数
function func(obj) {
obj.then(1, 2);
}
func({
then(resolve, reject) {
console.log(a, b);
},
});
const thenable = {
then(resolve, reject) {
console.log("then");
resolve("data");
},
};
Promise.resolve(thenable).then(
(data) => console.log(data),
(err) => console.log(err)
);
console.log(Promise.resolve(thenable));
*/
//2.Promise.reject()
//失败状态 Promise 的一种简写形式
Promise.reject("reason");
//参数 : 不管什么参数,都会原封不动地向后传递,作为后续方法的参数
const p = new Promise((resolve) => {
setTimeout(resolve, 1000, "我执行了");
});
Promise.reject(p).catch((err) =>console.log(err));
new Promise((resolve,reject) => {
resolve(123)
}).then(data=>{
// return data
//return Promise.resolve(data)
return Promise.reject('reason')
}).then(data=>{
console.log(data);
}).catch(err=>console.log(err))
Promise.all()[重点]
//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('reason')//p1 完成了 reason p2 完成了
});
const p2 = delay(1000).then(() => {
console.log("p2 完成了");
return "p2";
});
//Promise.all()的状态变化与所有传入的 Promise 实例对象状态有关
//所有状态都变成 resolved,最终的状态才会变成 resolved
//只要有一个变成 rejected,最终的状态就变成 rejected
const p = Promise.all([p1, p2]);
p.then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);
Promise.race() 和 Promise.allSettled()
const delay = (ms) => {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
};
const p1 = delay(1000).then(() => {
console.log("p1 完成了");
return "p1";
});
const p2 = delay(2000).then(() => {
console.log("p2 完成了");
return "p2";
});
//1.Promise.race()
//Promise.race()的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那么最终的就失败
const racePromise = Promise.race([p1, p2]);
racePromise.then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);
//2.Promise.allSettled()
//Promise.allSettled()的状态与传入的 Promise 状态无关,永远都是成功的,它只会忠实的记录下各个 Promise 的表现
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then((data) => {
console.log("succ", data);
});
Promise 的注意事项
//1.resolve 或 reject 函数执行后的代码
//推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码
new Promise((resolve, reject) => {
return resolve(123);
//return reject('reason');
});
//2.Promise.all/race/allSettled 的参数问题
//参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象
Promise.all([1, 2, 3]).then((datas) => {
console.log(datas); //(3) [1, 2, 3]
});
//等价于
// 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, 3])).then((datas) => {
console.log(datas); //(3) [1, 2, 3]
});
//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("reason");
})
// .catch((err) => {
// console.log("p1", err);
// }); //p1 reason
const p2 = delay(2000)
.then(() => {
console.log("p2 完成了");
return "p2"; //成功状态
// return Promise.reject('reason');
})
// .catch((err) => {
// console.log("p2", err);
// });
const allPromise = Promise.all([p1, p2]);
allPromise
.then((datas) => {
console.log(datas);
})
.catch((err) => {
console.log(err);
});
//错误既可以单独处理,也可以统一处理
//一旦被处理,就不会在其他地方再处理一遍
Promise 的应用[重点]
<style>
#img {
width: 80%;
padding: 10%;
}
</style>
</head>
<body>
<img
src="https://images.alphacoders.com/116/1162246.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://images7.alphacoders.com/116/1162253.jpg').then(img=>{
console.log(img.src);
setTimeout(()=>{
ImgDOM.src=img.src
},2000)
}).catch(err=>{
console.log(err);
})
</script>
Class 类
Class 是什么
//1.认识 Class: 人类:类
//具体的人:实例,对象
//类可以看做是对象的模板,用一个类可以创建出许多不同的对象
//2.Class 的基本用法: 类名一般首字母大写
// class Person() {} 错的
// class Person {}; 错的
class Person {
//实例化时执行构造方法,所以必须有构造方法
constructor(name, age) {
//建议还是写出来
console.log("实例化时执行构造方法");
this.name = name;
this.age = age;
// 一般在构造方法中定义属性,方法不在构造方法中定义
// this.speak = () => {};
}
//各实例共享的方法[所有方法写在这就可以了]
speak() {
console.log("speak");
}
}
const zs=new Person('ZS',18);
console.log(zs.name);
zs.speak();//speak
console.log(zs.speak===ls.speak);//true
//3.Class 与构造函数
console.log(typeof Person); //function
console.log(Person.prototype.speak); //ƒ speak() {console.log('speak');}
//(2)构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.speak=function (){};//方法
Class 的两种定义形式
//1.声明形式[重点关注]
class Person{
constructor() {
}
speak(){}
}
//2.表达式形式[知道了解就行]
const Person=function (){};
const Person=class {
constructor() {
console.log('constructor');
}
speak(){}
};
new Person();
(function (){
console.log('func');
})();
//立即执行的类
new (class {
constructor() {
console.log("constructor");
}
})();
实例属性、静态方法和静态属性
//1.实例属性[重点关注]会用会写就可以了
//方法就是值为函数的特殊属性
class Person {
age = 0; //默认值
sex = "male";
getSex = function () {
return this.sex;
};
constructor(name, sex) {
this.name = name;
this.sex = sex;
// this.age=18;↑
}
}
const p1 = new Person("slf");
console.log(p1.name);
console.log(p1.age);
//2.静态方法[重点关注]
//类的方法
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log("speak");
console.log(this);
}
//静态方法
static speak() {
console.log("人类可以说话");
//this 指向类本身
console.log(this); //Person
}
}
const p2 = new Person("ald");
p2.speak(); //speak
Person.speak(); //人类可以说话
//3.静态属性 [简单了解]知道怎么回事就行了
//类的属性
class Person {
constructor(name) {
this.name = name;
}
//不能这么写,目前只是提案,有兼容性问题
// static version='1.0';
static getVersion() {
return "1.0";
}
}
console.log(Person.getVersion());
私有属性和方法
//1.为什么需要私有属性和方法
//一般情况下,类的属性和方法都是公开的
//公有的属性和方法可以被外界修改,造成意想不到的错误
class Person {
constructor(name) {
this.name = name;
}
speak() {
console.log('speak');
}
getName(){
return this.name;
}
}
const p1 = new Person('Alex');
console.log(p.name);
p.speak();
//2.模拟私有属性和方法
//2.1 '_' [下划线]开头表示私有
class Person {
constructor(name) {
this._name = name;
}
speak() {
console.log('speak');
}
getName(){
return this._name;
}
}
const p2=new Person('LDde');
console.log(p.getName());//LDde
//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.getName());
})();
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); //指代父类的 constructor
}
}
const zss = new Programmer("zs", "男");
zss.say(); //say
zss.speak(); //speak
Programmer.speak(); //static speak
console.log(Programmer.version); //1.0
//2.改写继承的属性或方法
class Programmer extends Person {
constructor(name, sex, feature) {
super(name, sex); //this 操作不能放在 super 前面
this.feature = feature; //不能在 super 前面,是父类没有的
}
hi() {
console.log("hi");
}
//同名覆盖
speak() {
console.log("Programmer speak");
}
static speak() {
console.log("Programmer static speak");
}
}
Programmer.version = "2.0";
super[简单了解]
//1.作为函数调用
//代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错
//super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例
class Person {
constructor(name) {
this.name = name;
console.log(this); //指向子类的实例
}
}
class Programmer extends Person {
constructor(name, sex) {
super(name, sex); //相当于调用父类的构造方法
}
}
new Programmer();
//2.作为对象使用
//2.1 在构造方法中使用或一般方法中使用
//super 代表父类的原型对象 Person.prototype, 所以定义在父类实例上的方法或属性(this.),是无法通过 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(); //speak
}
speak() {
super.speak();
console.log("Programmer speak");
}
//2.2 在静态方法中使用
//指向父类,而不是父类的原型对象
//通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
static speak() {
super.speak();
console.log("Programmer speak");
}
}
// 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.speak);
}
}
Class 的应用 [键盘左右切换的幻灯片]
<link rel="stylesheet" href="./slider.css" />
<div class="slider-layout">
<div class="slider">
<div class="slider-content">
<div class="slider-item">
<a href="javascript:;"
><img src="./imgs/1.jpg" alt="1" class="slider-img"
/></a>
</div>
</div>
</div>
</div>
/* 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;
}
<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();
} else if (ev.keyCode === 39) {
// →
this.next();
}
});
}
}
new Slider(document.querySelector(".slider"), {
initialIndex: 1,
animation: true,
speed: 1000,
});
</script>
// 默认参数
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;
}
}