1.面向過程與面向對象
1.1面向過程
- 面向過程就是分析出解決問題所需要的步驟,然后用函數把這些步驟一步一步實現,使用的時候再一個一個的依次調用就可以了。
1.2面向對象
- 面向對象是把事務分解成為一個個對象,然后由對象之間分工與合作。
1.3面向過程與面向對象對比
面向過程 | 面向對象 | |
---|---|---|
優點 | 性能比面向對象高,適合跟硬件聯系很緊密的東西,例如單片機就采用的面向過程編程。 | 易維護、易復用、易擴展,由于面向對象有封裝、繼承、多態性的特性,可以設計出低耦合的系統,使系統 更加靈活、更加易于維護 |
缺點 | 不易維護、不易復用、不易擴展 | 性能比面向過程低 |
面向過程:蛋炒飯
面向對象:蓋澆飯
2.對象與類
對象根據類來創建
2.1對象
對象是由屬性和方法組成的:是一個無序鍵值對的集合,指的是一個具體的事物
- 屬性:事物的特征,在對象中用屬性來表示(常用名詞)
- 方法:事物的行為,在對象中用方法來表示(常用動詞)
2.1.1創建對象
//以下代碼是對對象的復習
//字面量創建對象
var ldh = {name: '劉德華',age: 18
}
console.log(ldh);//構造函數創建對象function Star(name, age) {this.name = name;this.age = age;}
var ldh = new Star('劉德華', 18)//實例化對象
console.log(ldh);
如上兩行代碼運行結果為:
2.2類
- 在 ES6 中新增加了類的概念,可以使用 class 關鍵字聲明一個類,之后以這個類來實例化對象。類抽象了對象的公共部分,它泛指某一大類(class)對象特指某一個,通過類實例化一個具體的對象
2.2.1創建類
- 語法:
//步驟1 使用class關鍵字
class name {// class body
}
//步驟2使用定義的類創建實例 注意new關鍵字
var xx = new name();
- 實例化類時構造函數自動執行
var count=0
/*類中的成員:屬性和方法*/
class Doctor {// 構造函數在根據類創建對象的時候自動調用執行constructor() {count++console.log('構造函數執行了'+count+'次')}
}
// 使用 new 關鍵字創建類的實例,或者叫做創建類的對象/* 可以將Doctor類看成iphone12的設計圖紙,new Doctor() 就是根據圖紙生產了一步iphone12手機*/
var d1=new Doctor()
// 又根據圖紙生產了一步iphone12手機
var d2=new Doctor()
// 又根據圖紙生產了一步iphone12手機
var d3=new Doctor()
以上代碼運行結果:
-
構造函數的作用、
構造函數的主要作用就是初始化類中的屬性,其實就是給類中的屬性賦值
2.2.2類創建添加屬性和方法
class Doctor {constructor(name, age, gender) {// this.name:類中的一個屬性叫做name;構造函數中的形參name是一個形參而已,完全可以取別的名字this.name = namethis.age = agethis.gender = gender}// 方法:手術shoushu() {console.log('我正在做手術')}// 方法:開藥:根據你的錢開藥kaiyao(money) {switch (money) {case 100:console.log('白蘿卜')break;case 200:console.log('胡蘿卜')break;case 300:console.log('人參')break;}}}
/*new Doctor() 就是在調用 Doctor 中的構造函數所以,構造函數需要幾個參數,我們就在Doctor后面的小括號中參數幾個參數,那么這幾個數值就會傳遞給構造函數的形參*/
var d1 = new Doctor('李文亮', 24, '男')
console.log(d1)
// 通過對象名稱.方法名稱() 的方式調用對象中的方法
d1.shoushu()
d1.kaiyao(300)
以上代碼運行結果:
注意喲:
- 通過class 關鍵字創建類, 類名我們還是習慣性定義首字母大寫
- 類里面有個constructor 函數,可以接受傳遞過來的參數,同時返回實例對象
- constructor 函數 只要 new 生成實例時,就會自動調用這個函數, 如果我們不寫這個函數,類也會自動生成這個函數
- 多個函數方法之間不需要添加逗號分隔
- 生成實例 new 不能省略
- 語法規范, 創建類 類名后面不要加小括號,生成實例 類名后面加小括號, 構造函數不需要加function
2.2.3類的繼承
子類能夠繼承父類中的成員(屬性、方法)
- 語法
// 父類
class Father{
} // 子類繼承父類
class Son extends Father {
}
- 示例
class Father {constructor(name) {this.name = name}speak() {console.log('father 在 speak')}
}class Son extends Father{}
// 創建Son類的對象
var s1=new Son()
console.log(s1);
s1.speak()
-
子類使用super關鍵字訪問父類的方法
首先:構造方法也會被子類繼承
子類可以定義自己的構造方法,在構造方法中擴展自己的屬性,但是一定要先調用父類的構造方法
class Father {constructor(name) {this.name = name}speak() {console.log('father 在 speak')}}/*子類會繼承父類中的所有的屬性和方法1)那么構造方法會像普通的方法一樣被繼承2)子類可以對父類進行擴展,比如添加新的屬性,但是一定要在添加之前先調用父類的構造方法*/class Son extends Father {constructor(name,age) {// 一定要在ti9anjia自己的屬性之前,先調用父類的構造方法super(name)// 添加自己的屬性this.age = age}}// 創建Son類的對象var s1 = new Son('yhb',18)console.log(s1);s1.speak()
再看一個案例
注意:
- 每個類都必須有構造函數,即使自己不創建,程序也會自動創建一個無慘的構造函數
- 構造函數也是可以繼承的
- 繼承中,子類調用一個方法,先看子類有沒有這個方法,如果有就先執行子類的,繼承中,如果子類里面沒有,就去查找父類有沒有這個方法,如果有,就執行父類的這個方法(就近原則),注意:上面所說的方法,既包括構造方法,也包括普通的方法
- 子類如果編寫了自己的構造方法,則在構造方法中必須先使用 super 調用父類的構造方法,也就是說super調用必須在this之前,否則就會產生錯誤
- 如果子類想要繼承父類的屬性,同時在自己內部擴展自己的屬性則在調用super之后,就可以編寫自己的代碼了
- 時刻注意this的指向問題,類里面的公有的屬性一定要加this使用.
- constructor中的this指向的是new出來的實例對象
- 自定義的方法,一般也指向的new出來的實例對象
- 綁定事件之后this指向的就是觸發事件的事件源
- 在 ES6 中類沒有變量提升,所以必須先定義類,才能通過類實例化對象
3.面向對象版tab 欄切換
3.1功能需求
- 點擊 tab欄,可以切換效果.
- 點擊 + 號, 可以添加 tab 項和內容項.
- 點擊 x 號, 可以刪除當前的tab項和內容項.
- 雙擊tab項文字或者內容項文字可以修改里面的文字內容
這里只有一個對象,就是tab欄,此欄目具有切換、刪除、增加和修改方法
3.2案例準備
- 獲取到標題元素
- 獲取到內容元素
- 獲取到刪除的小按鈕 x號
- 新建js文件,定義類,添加需要的屬性方法(切換,刪除,增加,修改)
- 時刻注意this的指向問題
3.3切換
-
為獲取到的標題綁定點擊事件,展示對應的內容區域,存儲對應的索引
this.lis[i].index = i;this.lis[i].onclick = this.toggleTab;
-
使用排他,實現只有一個元素的顯示
toggleTab() {//將所有的標題與內容類樣式全部移除for (var i = 0; i < this.lis.length; i++) {this.lis[i].className = '';this.sections[i].className = '';}//為當前的標題添加激活樣式this.className = 'liactive';//為當前的內容添加激活樣式that.sections[this.index].className = 'conactive';}
3.4添加
-
為添加按鈕+ 綁定點擊事件
this.add.onclick = this.addTab;
-
實現標題與內容的添加,做好排他處理
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();}
關于 insertadjancethtml 方法:
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/insertAdjacentHTML
總結:
1)使用 appendChild 方法,參數必須時一個節點,也就是說必須創建一個li節點,然后添加到ul中
2)使用insertadjaecehtml方法,就不用再創建一個節點了,因為其接受字符串作為參數
3.5刪除
-
為元素的刪除按鈕x綁定點擊事件
this.remove[i].onclick = this.removeTab;
-
獲取到點擊的刪除按鈕的所在的父元素的所有,刪除對應的標題與內容
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();}
3.6編輯
-
為元素(標題與內容)綁定雙擊事件
this.spans[i].ondblclick = this.editTab;this.sections[i].ondblclick = this.editTab;
-
在雙擊事件處理文本選中狀態,修改內部DOM節點,實現新舊value值的傳遞
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;};// 按下回車也可以把文本框里面的值給spaninput.onkeyup = function(e) {if (e.keyCode === 13) {// 手動調用表單失去焦點事件 不需要鼠標離開操作this.blur();}} }