前端学习笔记之 ES6之Promise与Class类(十二)

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值