第三十二節(jié): ES6 Class類(lèi)

1. ES6 Class 類(lèi)

ES6 提供了更接近傳統(tǒng)語(yǔ)言的寫(xiě)法,引入了 Class(類(lèi))這個(gè)概念,作為對(duì)象的模板。通過(guò)class關(guān)鍵字,可以定義類(lèi)。

ES6 的class可以看作只是一個(gè)語(yǔ)法糖,它的絕大部分功能,ES5 都可以做到,新的class寫(xiě)法只是讓對(duì)象原型的寫(xiě)法更加清晰、更像面向?qū)ο缶幊痰恼Z(yǔ)法而已。

1.1 . 復(fù)習(xí)構(gòu)造函數(shù)

類(lèi)的功能是創(chuàng)建對(duì)象的,js之前實(shí)現(xiàn)類(lèi)的功能需要利用構(gòu)造函數(shù)模擬類(lèi)的功能,通過(guò)new操作符實(shí)例化一個(gè)對(duì)象,對(duì)象上的原型[[Prototype]]上有個(gè)constructor方法指向構(gòu)造函數(shù)

function person(name,age) {     //構(gòu)造函數(shù)
  this.name=name;
  this.age=age  
}
person.prototype.sayName = function(){    //原型方法
  console.log(`我的名字叫${this.name}。`)
} 
person.prototype.sayAge = function(){    //原型方法
  console.log(`我今年${this.age}歲了`)
} 
person.run=function(){        //靜態(tài)方法
  console.log('靜態(tài)方法')
}
let student =new person('xiaoming',18)
console.log(student);
console.log(student.sayAge());
console.log(student.sayName());
console.log(person.run());   //靜態(tài)方法需要使用構(gòu)造函數(shù)調(diào)用

但是之前講了一個(gè)合并對(duì)象的,那么我們可以將新增的方法放在一個(gè)對(duì)象里合并到原型上


  <script type='module'>
    let Person = class {    //類(lèi)名為Person的ES6的類(lèi)的聲明
      //之前es5寫(xiě)的類(lèi)功能分為構(gòu)造函數(shù),原型方法,靜態(tài)方法,最終實(shí)例化,都是分開(kāi)的?,F(xiàn)在es6實(shí)現(xiàn)了整體的概念,將他們羅列到一個(gè)大括號(hào)容器里,其不是代碼塊,也不是對(duì)象,里面不加逗號(hào)。
      constructor(name, age) {  //構(gòu)造函數(shù)   
        this.name = name;
        this.age = age;
        this.aa = function () {          //實(shí)例對(duì)象上的方法,生成到實(shí)例對(duì)象上的方法
          console.log(這是實(shí)例方法);
        }
      };
      //除了constructor方法、使用關(guān)鍵字static的方法,其他方法都是原型上的方法
      sayName() {    //原型方法
        console.log(`我的名字叫${this.name}。`)
      };
      sayAge() {    //原型方法
        console.log(`我今年${this.age}歲了`)
      };
      static run() {     //關(guān)鍵字static 寫(xiě)的方法是靜態(tài)方法
        console.log(`這是靜態(tài)方法`);
      }
    }
    let student = new Person('李四', 20);    //通過(guò)類(lèi)實(shí)例化對(duì)象時(shí)本質(zhì)上執(zhí)行類(lèi)內(nèi)部的constructor函數(shù)
    console.log(student);
    student.sayAge()
    console.dir(Person)    //對(duì)象方式打印這個(gè)類(lèi),可以看到靜態(tài)方法
    Person.run()    //在這種類(lèi)里靜態(tài)方法只能通過(guò)類(lèi)名調(diào)用
    console.log(typeof Person);    //通過(guò)typeof檢測(cè)類(lèi)名結(jié)果是function,因此class聲明類(lèi)實(shí)際上是創(chuàng)建了一個(gè)具有構(gòu)造函數(shù)方法行為的函數(shù)
    console.log(Person.prototype);
  </script>

<script type='module'>
    let Person = class {    //匿名類(lèi)的表達(dá)式賦值給變量
     }
</script>
1.2 ES6 變形的類(lèi)Class
// 這是類(lèi) 不是json 所以里面不需要加逗號(hào)
class Person{
    // 這是一個(gè)構(gòu)造方法(函數(shù)),只要new Person() 這個(gè)構(gòu)造函數(shù)會(huì)自動(dòng)執(zhí)行
    constructor(name,age){ 
        console.log(`構(gòu)造函數(shù)執(zhí)行了,name:${name},age:${age}`) 
    }
    
    //以下是構(gòu)造函數(shù)原型上的方法
    
    showName(){
        return `名字:${this.name}`;
    }    // 不需要加逗號(hào),加逗號(hào)就報(bào)錯(cuò)
    
    showAge(){
        return `年齡:${this.age}`;
    }
}

// 這個(gè)Person也是要給函數(shù),typeof Person  打印function
let p1 = new Person("wuwei",18);
console.log(p1.showName());
console.log(p1.showAge());

表達(dá)式的類(lèi)(不推薦)

var Person = class {
    constructor(){
        this.name = "wuwei";
    }
    showName(){
        return `名字:${this.name}`
    }
}
2. constructor

constructor方法是類(lèi)的默認(rèn)方法,通過(guò)new命令生成對(duì)象實(shí)例時(shí),自動(dòng)調(diào)用該方法。一個(gè)類(lèi)必須有constructor方法,如果沒(méi)有顯式定義,一個(gè)空的constructor方法會(huì)被默認(rèn)添加。

class Fn {
}

// 等同于
class Fn {
  constructor() {}
}

constructor方法默認(rèn)返回實(shí)例對(duì)象(即this),完全可以指定返回另外一個(gè)對(duì)象。

class Foo {
  constructor() {
    return Object.create(null);
  }
}

new Foo() instanceof Foo
// false
//constructor函數(shù)返回一個(gè)全新的對(duì)象,結(jié)果導(dǎo)致實(shí)例對(duì)象不是Foo類(lèi)的實(shí)例。

3. 類(lèi)必須使用new調(diào)用

類(lèi)必須使用new調(diào)用,否則會(huì)報(bào)錯(cuò)。這是它跟普通構(gòu)造函數(shù)的一個(gè)主要區(qū)別,后者不用new也可以執(zhí)行。

class Foo {
  constructor() {
    return Object.create(null);
  }
}

Foo()
// TypeError: Class constructor Foo cannot be invoked without 'new'

4. 方法名字可以是變量

    var aaa = 'wu';
    class Person {
      constructor(name, age) {
        this.name = name;
        this.age = age;
      }
      [aaa]() {
        return `此時(shí)屬性名是一個(gè)變量,就是wu`
      }
    }

    // console.log(typeof Person)
    let p1 = new Person("wuwei", 18);
    console.log(p1.wu());   // 此時(shí)不能通過(guò)aaa調(diào)用函數(shù),aaa是變量,通過(guò)wu調(diào)用,可以通過(guò)[aaa]調(diào)用 p1[aaa]();
    console.log(p1[aaa]());

這里就可以發(fā)現(xiàn)屬性名可以為表達(dá)式

var aa = 'wu';
var bb = 'wei';

var obj = {
    [aa + bb]: "無(wú)為"
}

5. ES6 class類(lèi)沒(méi)有提升和預(yù)解析的功能

也就是說(shuō)如果寫(xiě)法會(huì)報(bào)錯(cuò)

var p1 = new Person()
console.log(p1);

class Person{
    constructor(){
        this.name = "aaa"
    }
}

但是以前通過(guò)ES5 構(gòu)造函數(shù)模擬類(lèi)是可以變量提升的,因?yàn)楸旧砭褪呛瘮?shù)

// 這種寫(xiě)法構(gòu)造函數(shù)會(huì)提升,正常打印一個(gè)對(duì)象
var p1 = new Person()
console.log(p1);
function Person(){
    this.name = "aaa"
}

6. get set

取值函數(shù) get 存值函數(shù) set

class Person{
    constructor(name,age){ 
        this.name = name;
        this.age = age;
    }
    get aa(){
        console.log(`成功獲取a的值`)
        return this.age;
    }
    set aa(val){
        console.log(`成功設(shè)置a的值為${val}`);
        this.age = val;
    }
}

var p1 = new Person('wuwei',18)

7.靜態(tài)方法

類(lèi)相當(dāng)于實(shí)例的原型,所有在類(lèi)中定義的方法,都會(huì)被實(shí)例繼承。

如果在一個(gè)方法前,加上static關(guān)鍵字,就表示該方法不會(huì)被實(shí)例繼承,而是直接通過(guò)類(lèi)來(lái)調(diào)用,這就稱(chēng)為“靜態(tài)方法”。

ES6 明確規(guī)定,Class 內(nèi)部只有靜態(tài)方法,沒(méi)有靜態(tài)屬性。

class Person{
    constructor(name,age){ 
        this.name = name;
        this.age = age;
    }
    showName(){
        return `這是通過(guò)構(gòu)造對(duì)象調(diào)用方法`;
    }
    static aaa(){
        return `這是靜態(tài)方法,是通過(guò)類(lèi)來(lái)調(diào)用的方法`;
    }
}

var p1 = new Person('wuwei',18)
console.log(p1.showName);
console.log(Person.aaa());

8. 繼承

子類(lèi)繼承父類(lèi)

8.1 之前的子類(lèi)繼承父類(lèi)
// 父類(lèi)
function Person(name){      //構(gòu)造函數(shù)
    this.name = name;
}
Person.prototype.showName = function(){   //構(gòu)造函數(shù)原型對(duì)象上的方法
    return `名字是:${this.name}`;
}

// 子類(lèi)
function Student(name,skill){
    Person.call(this,name);   // 繼承父類(lèi)的屬性 處理構(gòu)造函數(shù)的繼承,調(diào)用構(gòu)造函數(shù)的方法,同時(shí)改變this指向
    this.skill = skill;   
}
Student.prototype = new Person; // 繼承父類(lèi)的方法,繼承原型鏈
var stu = new Student("wuwei","逃課");
console.log(stu);
console.log(stu.name);
console.log(stu.showName())
8.2 ES6 類(lèi)的繼承 extends
class Person{   //基類(lèi)
  constructor(name){
    this.name = name;
  }
}
class Student extends Person{  //派生類(lèi)    es6的繼承通過(guò)關(guān)鍵字extends
  // constructor(name){    //這種繼承 沒(méi)法添加自己擴(kuò)展的屬性和方法,添加就報(bào)錯(cuò)
  //   this.name = name;
  // }         
}
let stu = new Student('張三')
console.log(stu);

以上這種繼承 沒(méi)法添加自己擴(kuò)展的屬性和方法,添加就報(bào)錯(cuò)

通過(guò)super()擴(kuò)展自己的屬性和方法,super就是調(diào)用基類(lèi)中的構(gòu)造函數(shù)

super注意點(diǎn):

1、super只可在派生類(lèi)的構(gòu)造函數(shù)中使用,如果嘗試在非派生類(lèi)(未使用extends的類(lèi))或函數(shù)中使用則會(huì)導(dǎo)致程序報(bào)錯(cuò)

2、在構(gòu)造函數(shù)中訪(fǎng)問(wèn)this之前一定要調(diào)用super(),因?yàn)閟uper()負(fù)責(zé)初始化this,如果在super之前訪(fǎng)問(wèn)this會(huì)導(dǎo)致報(bào)錯(cuò)

3、如果不想調(diào)用super(),則唯一的方法就是讓類(lèi)的構(gòu)造函數(shù)顯示返回一個(gè)對(duì)象

// 父類(lèi)
class Person{
    constructor(name){
        this.name = name;
    }
    showName(){
        console.log('父類(lèi)的showName方法');
        return `名字:${this.name}`;
    }
}

// 子類(lèi)
class Student extends Person{
    constructor(name,skill){
        super(name); // 先執(zhí)行父類(lèi)的屬性,繼承后在設(shè)置自己的屬性
        this.skill = skill;
    }
    // 繼承父類(lèi)的方法同時(shí)擴(kuò)展內(nèi)容
    showName(){
        super.showName(); // 先執(zhí)行父類(lèi)的showName方法
        // 在設(shè)置自己的showName方法
        console.log('執(zhí)行了子類(lèi)里面的showName');
    }

    // 設(shè)置自己的方法
    showSkill(){
        return `技能:${this.skill}`
    }
}

var stu = new Student("wuwei","逃課");
console.log(stu.name);
console.log(stu.showName())
  <script type='module'>
    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    class Student extends Person {
      constructor(name, age) {
         super(name)  ;       //super繼承基類(lèi)中的中的構(gòu)造函數(shù),修正初始化this,不能在super之前使用this
        // this.name = name;         //這個(gè)this.name不是繼承了父類(lèi)的this.name,不寫(xiě)是undefined。應(yīng)該把那么放到super中,相當(dāng)于擴(kuò)展,執(zhí)行super相當(dāng)于執(zhí)行基類(lèi)的構(gòu)造函數(shù)
        this.age = age;
      //  return { name, age }
      }
    }
    let stu = new Student('張三', 18)
    console.log(stu);
  </script>

類(lèi)方法的遮蔽,派生類(lèi)中的方法總會(huì)覆蓋基類(lèi)中的同名方法

 //遮蔽
 let a =10;
   function aa(){
     let a =20;
     function bb(){
       let a =30;
       console.log(a);
     }
     bb()     //如果aa()   bb()不執(zhí)行a是沒(méi)有值的
   }
   aa()
//派生類(lèi)遮蔽基類(lèi)中的方法
class Person {
      constructor(name) {
        this.name = name;
      }
      sayName() {
        return (`'基類(lèi)'中的${this.name}`);   //基類(lèi)  原型對(duì)象的原型上的方法
      }
    }
    class Student extends Person {
      constructor(name, age) {
        super(name);
        this.age = age;
      }
      sayName() {
        return (`'派生類(lèi)'中的${this.name}`);    //派生類(lèi)   原型對(duì)象上的方法
      }
    }
    let stu = new Student('張三', 18)
    console.log(stu);    //控制臺(tái)能看到原型上的方法  原型的原型sayName()方法
    console.log(stu.sayName());

如果想調(diào)用基類(lèi)中的方法,則可以通過(guò)super在派生類(lèi)同名方法中手動(dòng)調(diào)用基類(lèi)方法

    //派生類(lèi)擴(kuò)展基類(lèi)中的方法(調(diào)用基類(lèi)的方法)
    class Person {
      constructor(name) {
        this.name = name;
      }
      sayName() {
        console.log(`'基類(lèi)'中的${this.name}`);  
      }
    }
    class Student extends Person {
      constructor(name, age) {
        super(name);
        this.age = age;
      }
      sayName() {
        console.log(super.sayName()); //super超級(jí)的意思,代表的就是基類(lèi)。通過(guò)super取到基類(lèi)中的sayName(),先執(zhí)行基類(lèi)中的方法,再執(zhí)行派生類(lèi)中的同名方法
        console.log(`'派生類(lèi)'中的${this.name}`); 
      }
    }
    let stu = new Student('張三', 18)
    console.log(stu.sayName());

拖拽例子:

 <style>
    * {
      margin: 0;
      padding: 0;
    }

    #box {
      position: fixed;
      top: 0;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>

</head>

<body>
  <!-- es5的拖拽 -->
  <div id="box"></div>
  <script>
    let dixTop = 0, dixLeft = 0;//定義兩個(gè)信號(hào)量,記住top和left的值
    box.onmousedown = function (e) {// 有了值就可以綁定鼠標(biāo)點(diǎn)擊事件
      console.log(e);
      let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop;   //鼠標(biāo)到元素左上角的距離= 鼠標(biāo)距離頁(yè)面的距離 - 元素距離頁(yè)面左上角的距離
      console.log(dixL, dixT);
      box.onmousemove = function (e) { //鼠標(biāo)移動(dòng)事件
        dixLeft = e.clientX - dixL;
        dixTop = e.clientY - dixT;
        console.log(dixTop, dixLeft);
        this.style = `top:${dixTop}px;left:${dixLeft}px`
      }
      box.onmouseup = function () { //鼠標(biāo)松開(kāi)時(shí)還是跟著拖動(dòng),需要解綁自己和移動(dòng)事件
        box.onmouseup = null;
        box.onmousemove = null
      }
    }
  </script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    #box {
      position: fixed;
      top: 0;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>
</head>
<body>
  <!-- es5的拖拽 -->
  <div id="box"></div>
  <script>
    //拖動(dòng)速度過(guò)快時(shí),瀏覽器來(lái)不及繪制,鼠標(biāo)離開(kāi)了元素,無(wú)法觸發(fā)拖動(dòng)事件。把拖動(dòng)事件綁到document   樣式style的this為box,就算鼠標(biāo)脫落了元素,元素也會(huì)一直跟著鼠標(biāo)
    let dixTop = 0, dixLeft = 0;
    box.onmousedown = function (e) {
      console.log(e);
      let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop;
      console.log(dixL, dixT);
      document.onmousemove = function (e) {   //事件綁給document
        dixLeft = e.clientX - dixL;
        dixTop = e.clientY - dixT;
        console.log(dixTop, dixLeft);
        box.style = `top:${dixTop}px;left:${dixLeft}px`   //修改元素box的樣式
      }
      document.onmouseup = function () {     //解綁也要給document  里面為this
        this.onmouseup = null;
        this.onmousemove = null
      }
    }
  </script>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    #box {
      position: fixed;
      top: 0;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>
</head>

<body>
  <div id="box"></div>
  <script>
    //將box修改為this的寫(xiě)法,更優(yōu)化了
    let dixTop = 0, dixLeft = 0;
    box.onmousedown = function (e) {
      let dixL = e.clientX - dixLeft, dixT = e.clientY - dixTop;
      console.log(dixL, dixT);
      document.onmousemove = function (e) {
        dixLeft = e.clientX - dixL;
        dixTop = e.clientY - dixT;
        console.log(dixTop, dixLeft);
        this.style = `top:${dixTop}px;left:${dixLeft}px`   //這里也修改為this,要讓它的函數(shù)強(qiáng)制修改this
      }.bind(this)
      document.onmouseup = function () {
        this.onmouseup = null;
        this.onmousemove = null
      }
    }
  </script>
function Drag(className) {

    this.dom = document.createElement('div');
    this.dom.className = className;
    document.body.appendChild(this.dom)

    this.disX = 0;
    this.disY = 0;

    // 初始化
    this.init()

}

Object.assign(Drag.prototype, {
    // 初始化函數(shù)
    init() {
        this.dom.onmousedown = function (e) {
            this.down(e)

            document.onmousemove = this.move.bind(this)
            document.onmouseup = this.up
        }.bind(this)
    },

    down(e) {
        // console.log(e.clientX, e.clientY)
        this.disX = e.clientX - this.dom.offsetLeft;
        this.disY = e.clientY - this.dom.offsetTop;
        // console.log(this.disX, this.disY)
    },

    move(e) {
        // console.log(this)
        this.dom.style.left = e.clientX - this.disX + 'px';
        this.dom.style.top = e.clientY - this.disY + 'px';
    },

    up(e) {
        // console.log(this)
        document.onmousemove = null;
        document.onmouseup = null;
    }
})
let left = new Drag('left')

ES6 的拖拽(ES5拖拽不太適合給多個(gè)元素綁定拖拽,會(huì)很亂)

  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .abox,
    .bbox {
      position: fixed;
      top: 0;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
    .bbox {
      background-color: skyblue;
    }
  </style>
</head>

<body>
  <script>
    class Drag {       //類(lèi)
      constructor(className) {     //構(gòu)造函數(shù)
        this.dom = document.createElement('div');     //創(chuàng)建div節(jié)點(diǎn)
        this.dom.className = className;    //節(jié)點(diǎn)上添加屬性lassName,也就是參數(shù)
        document.body.appendChild(this.dom)    //放到頁(yè)面中body元素中
        this.disX = 0;     //信號(hào)量用的是屬性,變量不適合多個(gè)元素的拖拽,會(huì)發(fā)生覆蓋
        this.disY = 0;
        // 初始化代碼
        this.init()
      }
      // 初始化函數(shù)
      init() {
        this.dom.onmousedown = function (e) {
          this.down(e)   //差值
          document.onmousemove = this.move.bind(this)   //鼠標(biāo)移動(dòng)事件。不強(qiáng)制綁定this就是document
          document.onmouseup = this.up    //鼠標(biāo)抬起事件
        }.bind(this)    //強(qiáng)制綁定使內(nèi)部的this不再指向內(nèi)部的document,需要指向外部的this
      }
      down(e) {   //計(jì)算差值
        // console.log(e.clientX, e.clientY)
        this.disX = e.clientX - this.dom.offsetLeft;
        this.disY = e.clientY - this.dom.offsetTop;
        // console.log(this.disX, this.disY)
      }
      move(e) {
        // console.log(this)
        this.dom.style.left = e.clientX - this.disX + 'px';     //元素屬性值+px
        this.dom.style.top = e.clientY - this.disY + 'px';
      }
      up(e) {
        // console.log(this)
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
    let abox = new Drag('abox')     //對(duì)象,所有的屬性都是對(duì)象自己控制
    let bbox = new Drag('bbox')
    // bbox{
    //   dom:<div class="bbox"></div>,
    //   disX:0,
    //   dixY:0
    // }
  </script>

通過(guò)繼承創(chuàng)建有邊界的拖拽,拖拽控制在邊界內(nèi)部

    <style>
    * {
      margin: 0;
      padding: 0;
    }
    .abox,
    .bbox,
    .right {
      position: fixed;
      top: 0;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
    .bbox {
      background-color: skyblue;
    }

    .right {
      background-color: tomato;
    }
  </style>
</head>

<body>
  <script>
    class Drag {
      constructor(className) {
        this.dom = document.createElement('div');
        this.dom.className = className;
        document.body.appendChild(this.dom)
        this.disX = 0;
        this.disY = 0;
        this.init()
      }
      init() {
        this.dom.onmousedown = function (e) {
          this.down(e)
          document.onmousemove = this.move.bind(this)
          document.onmouseup = this.up
        }.bind(this)
      }
      down(e) {
        this.disX = e.clientX - this.dom.offsetLeft;
        this.disY = e.clientY - this.dom.offsetTop;
      }
      move(e) {
        this.dom.style.left = e.clientX - this.disX + 'px';
        this.dom.style.top = e.clientY - this.disY + 'px';
      }
      up(e) {
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
    let abox = new Drag('abox')
    let bbox = new Drag('bbox')
    
    
    class LimitDrag extends Drag {   //擴(kuò)展父類(lèi)的拖拽
      move(e) {
        super.move(e);   //繼承父類(lèi)
        this.dom.style.left = Math.max(0, this.dom.offsetLeft) + 'px';  //擴(kuò)展自己的邊界限定,不能超出邊界
        this.dom.style.left = Math.min(window.innerWidth - this.dom.offsetWidth, this.dom.offsetLeft) + 'px';

        this.dom.style.top = Math.max(0, this.dom.offsetTop) + 'px';
        this.dom.style.top = Math.min(window.innerHeight - this.dom.offsetHeight, this.dom.offsetTop) + 'px';
      }
    }
    let right = new LimitDrag('right')   //擴(kuò)展的拖拽
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容