1卫枝、面向?qū)ο缶幊?/h3>
什么是編程對象編程
面向?qū)ο笫且环N編程思想,經(jīng)常被拿來和面向過程比較位岔。
其中:
面向過程
關(guān)注的重點(diǎn)是動詞,是分析出解決問題需要的步驟堡牡,然后編寫函數(shù)實(shí)現(xiàn)每個步驟抒抬,最后依次調(diào)用函數(shù)。
而面向?qū)ο?/code>關(guān)注的重點(diǎn)是主謂晤柄,是把構(gòu)成問題的事物拆解為各個對象擦剑,而拆解出對象的目的也不是為了實(shí)現(xiàn)某個步驟,而是為了描述這個事物在當(dāng)前問題中的各種行為芥颈。
面向?qū)ο蟮奶攸c(diǎn)
面向?qū)ο蟮奶攸c(diǎn)是:封裝繼承多態(tài)
封裝
:讓使用對象的人不考慮內(nèi)部實(shí)現(xiàn)惠勒,只考慮功能使用 把內(nèi)部的代碼保護(hù)起來,只留出一些 api 接口供用戶使用
繼承
:就是為了代碼的復(fù)用爬坑,從父類上繼承出一些方法和屬性纠屋,子類也有自己的一些屬性
多態(tài)
:是不同對象作用于同一操作產(chǎn)生不同的效果。多態(tài)的思想實(shí)際上是把“想做什么”和“誰去做“分開
舉例
比如下棋的過程
- 面向過程是這樣的:開局 -> 白方下棋 -> 棋盤棋盤 -> 檢查勝負(fù) -> 黑方下棋 -> 棋盤棋盤 -> 檢查勝負(fù) ->.......循環(huán)
用代碼表示可能的一連串函數(shù)調(diào)用的
init();
whitePlay(); // 里面實(shí)現(xiàn)一遍下棋的操作
repaint(); // 棋盤展示
check();
blackPlay(); // 再單獨(dú)實(shí)現(xiàn)一遍下棋的操作
repaint(); // 棋盤展示
check();
- 面向?qū)ο蟊硎?重點(diǎn)是主謂):棋盤對象盾计,棋手
棋盤.開局 -> 選手.下棋 -> 棋盤.重新展示 -> 棋盤.檢查勝負(fù) -> 選手.下棋 -> 棋盤.重新展示 -> 棋盤.檢查勝負(fù)
用代碼表示可能是
const checkerBoard = new CheckerBoard(); // CheckerBoard 類內(nèi)部封賬了棋盤的操作售担,比如初始化棋盤,檢查勝負(fù)關(guān)系等
const whitePlayer = new Player('white'); // Player 類內(nèi)部封裝了各種玩家的操作署辉,比如等待族铆,落棋,悔棋
const blackPlayer = new Player('black');
whitePlayer.start();// emit()
在下棋過程中涨薪,面向?qū)ο蟮奶匦匀绻w現(xiàn)
- 封裝:Player, CheckerBoard 類,使用的時候并不需要知道內(nèi)部實(shí)現(xiàn)了什么炫乓,只需要考慮暴露出的 api 的使用
- 繼承:whitePlayer 和 blackPlayer 都繼承自 Player刚夺,都可以直接使用 Player 的各種方法和屬性
- 多態(tài):whitePlayer.start() 和 blackPlayer.start() 下棋的顏色分別是白色和黑色
什么時候適合使用面向?qū)ο笏枷?/h5>
- 在比較復(fù)雜的額問題面前献丑,或者說參與方比較多的時候∠拦茫可以很好的簡化問題创橄,更有利于擴(kuò)展和維護(hù)。
比如 現(xiàn)在下棋需要新增一個紅旗手莽红,面向?qū)ο笾恍枰?code>const redPlayer = new Player('red'), 而面向過程則需要再去整個過程的邏輯中再添加妥畏,很容易影響原有邏輯。
- 在比較簡單的問題安吁,可以一步步的按照步驟來調(diào)用醉蚁,其實(shí)也可以面向?qū)ο蟆?/li>
比如 現(xiàn)在下棋需要新增一個紅旗手莽红,面向?qū)ο笾恍枰?code>const redPlayer = new Player('red'), 而面向過程則需要再去整個過程的邏輯中再添加妥畏,很容易影響原有邏輯。
這只是一種思想,并不是一種約定鬼店,肯定是哪個方便用哪種网棍。
JS中的面向?qū)ο?/h5>
(1) 對象包含什么
屬性和方法。
(2) 內(nèi)置的對象
Object Array Date Function RegExp
(3) 創(chuàng)建對象
- 簡單方式
每一個新對象都要重新寫一遍 color 和 start 的賦值
const Player = new Object();
Player.color = "white";
Player.start = function () {
console.log("white下棋");
};
如果實(shí)現(xiàn)黑棋手妇智,就得再拷貝一份滥玷,太冗余
- 工廠模式
function createObject(color: string, start: () => void) {
const Player = new Object();
Player.color = color;
Player.start = start
return Player;
}
createObject('white');
createObject('black');
以上兩種方式都無法識別對象類型,比如 Player 的類型只是 Object
- 構(gòu)造函數(shù)/實(shí)例
function Player(color) {
this.color = color;
this.start = function () {
console.log(color + "下棋");
};
}
const whitePlayer = new Player("white");
const blackPlayer = new Player("black");
缺點(diǎn):
通過 this 添加的屬性和方法都會在內(nèi)存中復(fù)制一份巍棱,這樣就會造成內(nèi)存的浪費(fèi)惑畴。
好處:
但是這樣創(chuàng)建的好處是即使改變了某一個對象的屬性或方法,不會影響其他的對象(因?yàn)槊恳粋€對象都是復(fù)制的一份)
Tips. 怎么看函數(shù)是不是在內(nèi)存中創(chuàng)建了多次呢航徙?
比如 3. 構(gòu)造函數(shù)中如贷,我們可以看到 whitePlayer.start === blackPlayer.start
// 輸出 false
- 原型
function Player(color) {
this.color = color;
}
Player.prototype.start = function () {
console.log(color + "下棋");
};
const whitePlayer = new Player("white");
const blackPlayer = new Player("black");
優(yōu)點(diǎn): start在內(nèi)存中只存一份,通過原型繼承的方法并不是自身的捉偏,我們要在原型鏈上一層一層的查找倒得,這樣創(chuàng)建的好處是只在內(nèi)存中創(chuàng)建一次,實(shí)例化的對象都會指向這個 prototype 對象夭禽。
- 靜態(tài)屬性
是綁定在構(gòu)造函數(shù)上的屬性方法霞掺,需要通過構(gòu)造函數(shù)訪問
比如我們想看一下一共創(chuàng)建了多少個玩家的實(shí)例
function Player(color) {
this.color = color;
if (!Player.total) {
Player.total = 0;
}
Player.total++;
}
let p1 = new Player("white");
console.log(Player.total); // 1
let p2 = new Player("black");
console.log(Player.total); // 2
參考資料
面向?qū)ο蠛兔嫦蜻^程