Skip to content
目录

课程简介

学什么

初识 Class

Class 的属性和方法

Class 的继承

Class 的应用

初识 Class

Class 是什么

Class 的两种定义形式

Class 的属性和方法

实例属性、静态方法和静态属性

私有属性和方法

Class 的继承

extends

super

Class 的应用

幻灯片

1.初识 class

  • 认识 Class
  • Class 的基本用法
  • Class 与构造函数

1.Class 是什么

1.认识 Class

人类:类

具体的人:实例、对象

类可以看做是对象的模板,用一个类可以创建出许多不同的对象

2.Class 的基本用法

类名一般大写 不需要圆括号和分号

js
class Person {}
class Person() {} ×
class Person {}; ×
js
class Person {
    // 实例化时执行构造方法,所以必须有构造方法
    constructor(name, age) {
        console.log('实例化时执行构造方法');
        //this 代表实例对象,上面定义的是实例属性/方法
        this.name = name;
        this.age = age;

        // 一般在构造方法中定义属性,方法不在构造方法中定义
        //this.speak = () => {};
    }

    //speak:function(){}

    speak() {
        console.log('speak');
    }
}


const zs = new Person('ZS', 18);
const ls = new Person('LS', 28);
console.log(zs.name);
console.log(zs.age);
console.log(zs.speak;
console.log(ls.name);
console.log(ls.age);
console.log(ls.speak);
zs.speak();
console.log(zs.speak === ls.speak); //false

3.Class 与构造函数

js
//Class
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  speak() {
    console.log("speak");
  }
}

console.log(typeof Person);
console.log(Person.prototype.speak); //本质上是函数并且方法是绑定在原型之上

//构造函数
function Person(name, age) {
  this.name = name;
  this.age = age;
  //不在这里声明
  //this.speak = () => {};
}
Person.prototype.speak = function () {};

2.Class 的两种定义形式

  • 声明形式

  • 表达式形式

1.声明形式

js
class Person {
  constructor() {}

  speak() {}
}

2.表达式形式

js
function Person() {}
const Person = function () {};
//构造函数声明与表达式的形式

//匿名的类赋值给常量
const Person = class {
  constructor() {
    console.log("constructor");
  }
  speak() {}
};
new Person();

//立即执行函数
(function () {
  console.log("fn");
})();

new (class {
  constructor() {
    console.log("constructor");
  }
})();

2.属性与方法

1.实例属性和方法

js
class Person {
  age = 0;
  sex = "male";
  //方法就是值为函数的特殊属性
  getSex = function () {
    return this.sex;
  };

  constructor(name, sex) {
    this.name = name;
    //如果用户没有传递sex值,那么就是用默认值的
    //实例化时传入的值,那么就使用传入的值
    //实例化时没有传入值,那么就使用默认的值
    this.sex = sex || this.age;
  }
  //实例方法
  speak() {
    console.log("speak");
  }
}

const p = new Person('"yunmu"', "male");
console.log(p.name);
console.log(p.age);

2.静态方法

类的方法

js
class Person {
  age = 0;
  sex = "male";

  constructor(name, sex) {
    this.name = name;
    this.sex = sex;
  }

  speak() {
    this.age = 18;
    console.log(this); //指向对象
  }

  //静态方法  建议这一种
  static speak() {
    console.log("人类可以说话");
    console.log(this); // this 指向类
  }
}

//静态方法 第二种定义
Person.speak = function () {
  console.log("人类可以说话");
  console.log(this);
};

const p = new Person("Alex");
p.speak();
Person.speak(); //不会冲突

3.静态属性

类的属性

js
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());

4.私有属性和方法

1.为什么需要私有属性和方法

一般情况下,类的属性和方法都是公开的

公有的属性和方法可以被外界修改,造成意想不到的错误

js
class Person {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log("speak");
  }
  //通过方法去获取name  不通过属性
  getName() {
    return this.name;
  }
}
const p = new Person("yunmu");
console.log(p.name);
p.speak();

// ....
// p.name = 'zs';
// console.log(p.name);

2.模拟私有属性和方法

1.开头表示私有

js
class Person {
  constructor(name) {
    this._name = name;
  }

  speak() {
    console.log("speak");
  }

  getName() {
    return this._name;
  }
}
const p = new Person("yunmu");
// console.log(p._name);
p.name = "zd";
console.log(p.getName());

2.将私有属性和方法移出类

js
(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("yunmu");
  console.log(p.name);
  //只能通过这样访问
  console.log(p.getName());
})();

3.继承

extends

  • 子类继承父类
  • 改写继承的属性或方法

1.子类继承父类

js
//父类
class People {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} eat something`);
  }

  static speak() {
    console.log(`人类的本质都是复读机`);
  }
}

//子类
class Student extends People {
  constructor(name, number) {
    super(name);
    //this.name = name;
    this.number = number;

    //this.gender = "female";
  }
  sayHi() {
    console.log(`姓名${this.name} 学号${this.number}`);
  }
}

//子类
class Teacher extends People {
  constructor(name, major) {
    super(name);
    this.major = major;
  }

  teach() {
    console.log(`${this.name} 教授 ${this.major}`);
  }
}

//学生实例
const liyujiao = new Student("李玉俏", 100);
console.log(liyujiao);
console.log(liyujiao.name);
console.log(liyujiao.number);
liyujiao.sayHi();

//学生实例
//const lindaiyu = new Student("林黛玉", 101);
//console.log(lindaiyu);
//console.log(lindaiyu.name);
//console.log(lindaiyu.number);
//lindaiyu.sayHi();

//老师实例
const jiayucun = new Teacher("贾雨村", "诗词格律");
console.log(jiayucun);
console.log(jiayucun.name);
console.log(jiayucun.major);
jiayucun.teach();
jiayucun.eat();

2.改写继承的属性或方法

js
//父类
class People {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(`${this.name} eat something`);
  }

  static speak() {
    console.log(`人类的本质都是复读机`);
  }
}

//子类
class Student extends People {
  constructor(name, number) {
    super(name);
    //this.name = name;
    this.number = number;

    //this.gender = "female";
  }
  sayHi() {
    console.log(`姓名${this.name} 学号${this.number}`);
  }
}

//子类
class Teacher extends People {
  constructor(name, major) {
    super(name);
    //this 操作不能放在 super 前面
    this.major = major;
  }

  teach() {
    console.log(`${this.name} 教授 ${this.major}`);
  }

  //同名覆盖
  eat() {
    console.log(`${this.name} 在吃饭`);
  }

  //同名覆盖
  static speak() {
    console.log("学者必求师 为师不可不谨也");
  }
}

//学生实例
const liyujiao = new Student("李玉俏", 100);
console.log(liyujiao);
console.log(liyujiao.name);
console.log(liyujiao.number);
liyujiao.sayHi();

//学生实例
//const lindaiyu = new Student("林黛玉", 101);
//console.log(lindaiyu);
//console.log(lindaiyu.name);
//console.log(lindaiyu.number);
//lindaiyu.sayHi();

//老师实例
const jiayucun = new Teacher("贾雨村", "诗词格律");
console.log(jiayucun);
console.log(jiayucun.name);
console.log(jiayucun.major);
jiayucun.teach();
jiayucun.eat();
Teacher.speak();

super

  • 作为函数调用
  • 作为对象使用
  • 注意事项

1.作为函数调用

代表父类的构造方法,只能用在子类的构造方法中,用在其他地方就会报错

super 虽然代表了父类的构造方法,但是内部的 this 指向子类的实例

js
//父类
class People {
  constructor(name) {
    console.log(this);
    this.name = name;
  }

  eat() {
    console.log(`${this.name} eat something`);
  }

  static speak() {
    console.log(`人类的本质都是复读机`);
  }
}

//子类
class Student extends People {
  constructor(name, number) {
    super(name);
    this.number = number;
  }

  sayHi() {
    //super(); // ×
    console.log(`姓名${this.name} 学号${this.number}`);
  }
}

//学生实例
const liyujiao = new Student("李玉俏", 100);
console.log(liyujiao.name);
console.log(liyujiao.number);
liyujiao.sayHi();

2.作为对象使用

js
//父类
class People {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(this);
    console.log(`${this.name} eat something`);
  }

  static speak() {
    console.log(this);
    console.log(`人类的本质都是复读机`);
  }
}
/* 
        1.在构造方法中使用或一般方法中使用

        super 代表父类的原型对象 Person.prototype

        所以定义在父类实例上的方法或属性,是无法通过 super 调用的

        通过 super 调用父类的方法时,方法内部的 this 指向当前的子类实例

      */
//子类
class Student extends People {
  constructor(name, number) {
    super(name);
    this.number = number;

    //作为对象在构造方法中调用
    //super.eat();
  }

  sayHi() {
    console.log(`姓名${this.name} 学号${this.number}`);
  }

  eat() {
    //作为对象在一般方法中调用
    super.eat();
    console.log("吃大口");
  }
  //2.在静态方法中使用
  //指向父类,而不是父类的原型对象
  //通过 super 调用父类的方法时,方法内部的 this 指向当前的子类,而不是子类的实例
  static speak() {
    super.speak();
    console.log(`我是${this.name} 我正在学习!`);
  }
}

//学生实例
const liyujiao = new Student("李玉俏", 100);
console.log(liyujiao.name);
console.log(liyujiao.number);
liyujiao.sayHi();
liyujiao.eat();
Student.speak();

3.注意事项

使用 super 的时候,必须显式指定是作为函数还是作为对象使用,否则会报错

js
//父类
class People {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(this);
    console.log(`${this.name} eat something`);
  }

  static speak() {
    console.log(this);
    console.log(`人类的本质都是复读机`);
  }
}

//子类
class Student extends People {
  constructor(name, number) {
    super(name);
    this.number = number;

    //console.log(super); // ×
    //console.log(super());
    //console.log(super.speak);
  }
}

4.Class 的应用(轮播图)

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Class 的应用</title>
    <style>
      /* css reset */
      * {
        padding: 0;
        margin: 0;
      }
      a {
        text-decoration: none;
        outline: none;
      }
      img {
        width: 100%;
        vertical-align: top;
      }

      /* layout */
      .slider-layout {
        width: 80%;
        height: 420px;
        margin: 100px 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;
      }
    </style>
  </head>
  <body>
    <div class="slider-layout">
      <div class="slider">
        <div class="slider-content">
          <div class="slider-item">
            <a href="javascript:;"
              ><img
                src="https://raw.githubusercontent.com/xixixiaoyu/CloundImg2/main/Dny8yV.jpg"
                alt="1"
                class="slider-img"
            /></a>
          </div>
          <div class="slider-item">
            <a href="javascript:;"
              ><img
                src="https://raw.githubusercontent.com/xixixiaoyu/CloundImg2/main/DnytwF.jpg"
                alt="1"
                class="slider-img"
            /></a>
          </div>
          <div class="slider-item">
            <a href="javascript:;"
              ><img
                src="https://raw.githubusercontent.com/xixixiaoyu/CloundImg2/main/DnyaFJ.jpg"
                alt="1"
                class="slider-img"
            /></a>
          </div>
          <div class="slider-item">
            <a href="javascript:;"
              ><img
                src="https://raw.githubusercontent.com/xixixiaoyu/CloundImg2/main/DnywWR.jpg"
                alt="1"
                class="slider-img"
            /></a>
          </div>
        </div>
      </div>
    </div>

    <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;
        }
      }
    </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>
  </body>
</html>

Dny8yV.jpg

DnytwF.jpg

DnywWR.jpg

课程总结

Class 的基本用法

Dy2EHe.png

Class 的两种定义形式

声明形式

Dy2c59.png

表达式形式

Dy2IbD.png

DyRk2q.png

实例属性、静态方法和静态属性

DyRfij.png

私有属性和方法

_开头表示私有

将私有属性和方法移出类

DyWFTe.png

extends

使用 extends 可以实现继承

可以改写继承到的属性或方法,同名覆盖

super

作为函数调用

作为对象使用

使用 super 的时候,必须显式指定作为函数还是作为对象使用

super 作为函数调用

super 代表父类的构造方法,只能用在子类的构造方法中

内部的 this 指向子类的实例

super 作为对象使用

在构造方法和一般方法中使用

super 代表父类的原型对象

通过 super 调用父类的方法时,方法的 this 指向当前的子类实例

在静态方法中使用

super 代表父类

通过 super 调用父类的方法时,方法的 this 指向当前的子类

May all encounters not be in vain