深入瞭解JavaScript之面向對象(二)之 class

class是什麼
  • class是定義類的方法。ES6之前用構造函數的方式定義類,ES6引入了class。
  • class是語法糖。
  • class內部默認是嚴格模式。
  • class不存在變量提升現象,必須要先聲明後使用
使用

傳統方式定義一個類

function Point1(x,y){
  this.x = x
  this.y = y
}
Point1.prototype.add = function(){
  return this.x + this.y
}
var p1 = new Point1(3,9)
console.log(p1.add())

class方式定義一個類

class Point2{
  constructor(x, y){
    this.x = x
    this.y = y
  }
  add(){  // 這裏的add方法實際上是定義在原型上的。
    return this.x + this.y
  }
}
var p2 = new Point2(4,77)
console.log(p2.add())

類的數據類型

console.log(typeof Point1)  // function
console.log(typeof Point2)  // function
this指向問題

如果方法裏有this,this指向的是實例

類的數據類型就是函數,類本身就是指向函數的
類的所有方法都是定義在類的prototype上的

類的私有變量
  • 私有變量就是只能在類內部訪問的變量,外部(類的實例化對象)無法訪問的變量。
  • 子類不能繼承父類的私有變量。

私有變量的實現

  • 私有變量定義在constructor 外面,並且加上#號;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            position: absolute;
            left: 100px;
            top: 100px;
            width: 100px;
            height: 100px;
            background: red;
        }
        #box2 {
            position: absolute;
            left: 200px;
            top: 200px;
            width: 100px;
            height: 100px;
            background: green;
        }
    </style>
</head>
<body>
    <div id="box">文字</div>
    <div id="box2">文字</div>
<script>
/*
    抽象:
        拖動之後元素跟隨移動
    封裝: 
        封裝成類    
*/
class Drag {
    startMouse = {};
    startEl = {};
    #el = null;//私有變量,在實例化對象中不能獲取
    constructor(el){
        this.#el = el;
        this.start();
    }
    start(){
        let move = (e)=>{
            this.move(e);
        };
       this.#el.addEventListener("mousedown",(e)=>{
            this.startMouse = {
                x: e.clientX,
                y: e.clientY
            };
            this.startEl= this.getOffset();
           // console.log(this.ondragstart);
            this.ondragstart&&this.ondragstart(e);//start執行纔會去執行ondragstart,所以ondragstart依賴於start 
            document.addEventListener("mousemove",move);
            document.addEventListener("mouseup",()=>{
                document.removeEventListener("mousemove",move);
                this.end();
            },{once: true});
            e.preventDefault();
       }); 
    }
    move(e){
        let nowMouse = {
            x: e.clientX,
            y: e.clientY
        };
        let disMouse = {
            x: nowMouse.x - this.startMouse.x,
            y: nowMouse.y - this.startMouse.y
        };
        this.setOffset(disMouse);
        this.ondrag&&this.ondrag(e);
    }
    //拖拽結束
    end(e){
        this.ondragend&&this.ondragend(e);
    }
    // 獲取元素的位置
    getOffset(){
        return {
            x: parseFloat(getComputedStyle(this.#el)["left"]),
            y: parseFloat(getComputedStyle(this.#el)["top"])
        }
    }
    // 設置元素的位置
    setOffset(dis){
        this.#el.style.left = dis.x + this.startEl.x + "px";
        this.#el.style.top = dis.y + this.startEl.y + "px"
    }
}
{
    let box = document.querySelector("#box");
    let box2 = document.querySelector("#box2");
    let d = new Drag(box);
    let d2 = new Drag(box2);
    let box2Clone = null;
    d2.ondragstart = function(){
        //console.log("開始拖拽");
        box2Clone = box2.cloneNode(true);
        //console.log(box2Clone);
        document.body.appendChild(box2Clone);
        box2.style.opacity = .5;
    };
    d2.ondrag = function(){
        //console.log("拖拽中");
    };
    d2.ondragend = function(){
        document.body.removeChild(box2Clone);
        box2.style.opacity = 1;
    };
}
</script>    
</body>
</html>

爲什麼要使用私有變量

  let d = new Drag(box);
  d.el = box2;
  上面的這個例子中,假如我們已經對一個元素實例化了,但是我們改變實例化對象的el 屬性的話,那對el的這個拖拽就無法生效了,
  el這個屬性必須是私有屬性。
類的繼承

繼承:繼承可以使得子類具有父類的屬性和方法並重新定義、追加屬性和方法等。

class Person {
    constructor(name,age){
        this.name = name;
        this.age = age;
    }
    say(){
        console.log(this.name);
    }
}
class Teacher extends Person {
    constructor(name,age){
        // 如果在子類中重新定義constructor,一定在這裏調用 super
        super(name,age);
        this.speciality = "講課";
    }
    skill(){
        console.log("連續上課24小時");
    }
}
console.log(new Person("小明",18));
console.log(new Teacher("小紅",58));
類的靜態屬性

關於類有兩個概念,1,類自身 ;2,類的實例對象
總的來說:靜態的是指向類自身,而不是指向實例對象,主要是歸屬不同,這是靜態屬性的核心。

靜態方法的使用:在方法前加上static關鍵字

class Foo {
  static classMethod() {
    return 'hello';
  }
}

爲什麼使用靜態方法:阻止方法被實例繼承,類的內部相當於實例的原型,所有在類中直接定義的方法相當於在原型上定義方法,都會被類的實例繼承,但是使用static靜態方法定義的不會被實例繼承,而且可以被實例直接應用Foo.classMethod(),此時寫成new Foo.classMethod()會提示不存在此方法

靜態方法中this指向:this指向類而不是類的實例

class Foo {
  static bar () {
    this.baz();
  }
  static baz () {
    console.log('hello');
  }
  baz () {
    console.log('world');
  }
}
 
Foo.bar() // hello
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章