1. 程式人生 > 實用技巧 >ES6面向物件——class類

ES6面向物件——class類

面相過程的程式設計思想

面向過程就是分析出解決問題所需要的步驟,然後用函式把這些步驟一步一步實現,使用的時候再一個一個的依次呼叫就可以了。
面向過程,就是按照我們分析好了的步驟,按照步驟解決問題。

面向物件程式設計 OOP (Object Oriented Programming)

面向物件是把事務分解成為一個個物件,然後由物件之間分工與合作。
在面向物件程式開發思想中,每一個物件都是功能中心,具有明確分工。
面向物件程式設計具有靈活、程式碼可複用、容易維護和開發的優點,更適合多人合作的大型軟體專案。
面向物件的特性:

  • 封裝性
  • 繼承性
  • 多型性

面向過程和麵向物件的對比

面相過程

優點:效能比面向物件高,適合跟硬體聯絡很緊密的東西,例如微控制器就採用的面向過程程式設計。
缺點:沒有面向物件易維護、易複用、易擴充套件

面向物件

優點:易維護、易複用、易擴充套件,由於面向物件有封裝、繼承、多型性的特性,可以設計出低耦合的系統,使系統 更加靈活、更加易於維護
缺點:效能比面向過程低

es6中的類和物件

面向物件的思維特點:

抽取(抽象)物件共用的屬性和行為組織(封裝)成一個類(模板)
對類進行例項化, 獲取類的物件
面向物件程式設計我們考慮的是有哪些物件,按照面向物件的思維特點,不斷的建立物件,使用物件,指揮物件做事情.

物件

現實生活中:萬物皆物件,物件是一個具體的事物,看得見摸得著的實物。例如,一本書、一輛汽車、一個人可以是“物件”,一個數據庫、一張網頁、一個與遠端伺服器的連線也可以是“物件”。
在 JavaScript 中,物件是一組無序的相關屬性和方法的集合,所有的事物都是物件,例如字串、數值、陣列、函式等。
物件是由屬性和方法組成的:

  • 屬性:事物的特徵,在物件中用屬性來表示(常用名詞)
  • 方法:事物的行為,在物件中用方法來表示(常用動詞)

類 class

在 ES6 中新增加了類的概念,可以使用 class 關鍵字宣告一個類,之後以這個類來例項化物件。
類抽象了物件的公共部分,它泛指某一大類(class)
物件特指某一個,通過類例項化一個具體的物件

建立類

class name {
  // class body
}

var xx = new name(); // 注意: 類必須使用 new 例項化物件

類 constructor 建構函式

constructor() 方法是類的建構函式(預設方法),用於傳遞引數,返回例項物件,通過 new 命令生成物件例項時,自動呼叫該方法。如果沒有顯示定義, 類內部會自動給我們建立一個constructor()

class Person {
  constructor(name,age) {   // constructor 構造方法或者建構函式
      this.name = name;
      this.age = age;
    }
  say() {
      console.log(this.name + '你好');
   }
// 注意: 方法之間不能加逗號分隔,同時方法不需要新增 function 關鍵字。
}  
var ldh = new Person('劉德華', 18); 
console.log(ldh.name) 

繼承

現實中的繼承:子承父業,比如我們都繼承了父親的姓。
程式中的繼承:子類可以繼承父類的一些屬性和方法。

class Father{   // 父類
} 
class  Son extends Father {  // 子類繼承父類
}

super關鍵字

super 關鍵字用於訪問和呼叫物件父類上的函式。可以呼叫父類的建構函式,也可以呼叫父類的普通函式

class Person {   // 父類
      constructor(surname){
         this.surname = surname;
     }
} 
class  Student extends Person {       // 子類繼承父類
     constructor(surname,firstname){
          super(surname);             // 呼叫父類的constructor(surname)
	this.firstname = firstname; // 定義子類獨有的屬性
     }
}   

** 注意: **子類在建構函式中使用super, 必須放到 this 前面 (必須先呼叫父類的構造方法,在使用子類構造方法)

class Father {
   constructor(surname) {
       this.surname = surname;
    }
   saySurname() {
     console.log('我的姓是' + this.surname);
   }
}
class Son extends Father { // 這樣子類就繼承了父類的屬性和方法
   constructor(surname, fristname) {
        super(surname);   // 呼叫父類的constructor(surname)
        this.fristname = fristname;
    }
   sayFristname() {
        console.log("我的名字是:" + this.fristname);

   }
}
var damao = new Son('劉', "德華");
damao.saySurname();
damao.sayFristname(); 

super關鍵字 用於訪問和呼叫物件父類上的函式。可以呼叫父類的建構函式,也可以呼叫父類的普通函式。

class Father {
     say() {
         return '我是爸爸';

     }
}
class Son extends Father { // 這樣子類就繼承了父類的屬性和方法
     say() {
          // super.say()  super 呼叫父類的方法
          return super.say() + '的兒子';
     }
}
var damao = new Son();
console.log(damao.say());    

注意點

  • 在 ES6 中類沒有變數提升,所以必須先定義類,才能通過類例項化物件.
  • 類裡面的共有屬性和方法一定要加this使用.
  • 類裡面的this指向問題.
  • constructor 裡面的this指向例項物件, 方法裡面的this 指向這個方法的呼叫者

面向物件案例——面向物件版 tab 欄切換

功能需求:

  • 點選 tab欄,可以切換效果.
  • 點選 + 號, 可以新增 tab 項和內容項.
  • 點選 x 號, 可以刪除當前的tab項和內容項.
  • 雙擊tab項文字或者內容項文字,可以修改裡面的文字內容.

抽象物件: Tab 物件

  • 該物件具有切換功能
  • 該物件具有新增功能
  • 該物件具有刪除功能
  • 該物件具有修改功能

新增功能

  • 點選 + 可以實現新增新的選項卡和內容
  • 第一步: 建立新的選項卡li 和 新的 內容 section
  • 第二步: 把建立的兩個元素追加到對應的父元素中.
  • 以前的做法: 動態建立元素 createElement , 但是元素裡面內容較多, 需要innerHTML賦值,在 appendChild 追加到父元素裡面.
  • 現在高階做法: 利用insertAdjacentHTML()可以直接把字串格式元素新增到父元素中
  • appendChild 不支援追加字串的子元素, insertAdjacentHTML 支援追加字串的元素
  • insertAdjacentHTML(追加的位置,‘要追加的字串元素’)
  • 追加的位置有: beforeend 插入元素內部的最後一個子節點之後
  • 該方法地址: https://developer.mozilla.org/zh-CN/docs/Web/API/Element/insertAdjacentHTML

刪除功能

點選 × 可以刪除當前的li選項卡和當前的section
X是沒有索引號的, 但是它的父親li 有索引號, 這個索引號正是我們想要的索引號
所以核心思路是: 點選 x 號可以刪除這個索引號對應的 li 和 section
但是,當我們動態刪除新的li和索引號時,也需要重新獲取 x 這個元素. 需要呼叫init 方法

編輯功能

  • 雙擊選項卡li或者 section裡面的文字,可以實現修改功能
  • 雙擊事件是: ondblclick
  • 如果雙擊文字,會預設選定文字,此時需要雙擊禁止選中文字
    * window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
  • 核心思路: 雙擊文字的時候, 在 裡面生成一個文字框, 當失去焦點或者按下回車然後把文字框輸入的值給原先元素即可.

原始碼(js實現邏輯)

var that;
class Tab {
    constructor(id) {
        // 獲取元素
        that = this;
        this.main = document.querySelector(id);
        this.add = this.main.querySelector('.tabadd');
        // li的父元素
        this.ul = this.main.querySelector('.fisrstnav ul:first-child');
        // section 父元素
        this.fsection = this.main.querySelector('.tabscon');
        this.init();
    }
    init() {
            this.updateNode();
            // init 初始化操作讓相關的元素繫結事件
            this.add.onclick = this.addTab;
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].index = i;
                this.lis[i].onclick = this.toggleTab;
                this.remove[i].onclick = this.removeTab;
                this.spans[i].ondblclick = this.editTab;
                this.sections[i].ondblclick = this.editTab;

            }
        }
        // 因為我們動態新增元素 需要從新獲取對應的元素
    updateNode() {
            this.lis = this.main.querySelectorAll('li');
            this.sections = this.main.querySelectorAll('section');
            this.remove = this.main.querySelectorAll('.icon-guanbi');
            this.spans = this.main.querySelectorAll('.fisrstnav li span:first-child');
        }
        // 1. 切換功能
    toggleTab() {
            // console.log(this.index);
            that.clearClass();
            this.className = 'liactive';
            that.sections[this.index].className = 'conactive';
        }
        // 清除所有li 和section 的類
    clearClass() {
            for (var i = 0; i < this.lis.length; i++) {
                this.lis[i].className = '';
                this.sections[i].className = '';
            }
        }
        // 2. 新增功能
    addTab() {
            that.clearClass();
            // (1) 建立li元素和section元素
            var random = Math.random();
            var li = '<li class="liactive"><span>新選項卡</span><span class="iconfont icon-guanbi"></span></li>';
            var section = '<section class="conactive">測試 ' + random + '</section>';
            // (2) 把這兩個元素追加到對應的父元素裡面
            that.ul.insertAdjacentHTML('beforeend', li);
            that.fsection.insertAdjacentHTML('beforeend', section);
            that.init();
        }
        // 3. 刪除功能
    removeTab(e) {
            e.stopPropagation(); // 阻止冒泡 防止觸發li 的切換點選事件
            var index = this.parentNode.index;
            console.log(index);
            // 根據索引號刪除對應的li 和section   remove()方法可以直接刪除指定的元素
            that.lis[index].remove();
            that.sections[index].remove();
            that.init();
            // 當我們刪除的不是選中狀態的li 的時候,原來的選中狀態li保持不變
            if (document.querySelector('.liactive')) return;
            // 當我們刪除了選中狀態的這個li 的時候, 讓它的前一個li 處於選定狀態
            index--;
            // 手動呼叫我們的點選事件  不需要滑鼠觸發
            that.lis[index] && that.lis[index].click();
        }
        // 4. 修改功能
    editTab() {
        var str = this.innerHTML;
        // 雙擊禁止選定文字
        window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
        // alert(11);
        this.innerHTML = '<input type="text" />';
        var input = this.children[0];
        input.value = str;
        input.select(); // 文字框裡面的文字處於選定狀態
        // 當我們離開文字框就把文字框裡面的值給span
        input.onblur = function() {
            this.parentNode.innerHTML = this.value;
        };
        // 按下回車也可以把文字框裡面的值給span
        input.onkeyup = function(e) {
            if (e.keyCode === 13) {
                // 手動呼叫表單失去焦點事件  不需要滑鼠離開操作
                this.blur();
            }
        }
    }

}
new Tab('#tab');