面向?qū)ο缶幊?/h2>
面向過程:關(guān)注的重點在動詞廷没。分析出解決問題的步驟糊饱,然后用函數(shù)來實現(xiàn)這些步驟,使用的時候一次調(diào)用颠黎。
面向?qū)ο螅宏P(guān)注的重點在主謂另锋。把構(gòu)成問題的事物分解成各個對象,構(gòu)建對象的目的不是為了完成步驟狭归,而是為了描述事物在整個解決問題的過程中的行為夭坪。
面向?qū)ο蟮奶攸c是什么?
封裝:部分屬性和行為是對象私有的唉铜,不提供給對象訪問台舱,只提供部分對外接口,防止對象核心數(shù)據(jù)被篡改潭流【和铮可以在一定程度上保證對象的完整性和功能的健全。
繼承:為了代碼的復(fù)用灰嫉,子類可以從父類繼承一些屬性和方法拆宛。當(dāng)然子類也可以有自己獨有的額外屬性和方法。
多態(tài):不同對象 作用的 同一行為 產(chǎn)生的 不同的效果讼撒。把 做什么 和 誰去做 分開了浑厚。不同對象復(fù)用了同一行為。
比如下棋的過程:
面向過程的:開局 - 黑方下棋 — 展示畫布 — 檢查勝負(fù) - 白方下棋 - 展示畫布 - 檢查勝負(fù) -------- 循環(huán)
用代碼表示:
init()
blackPlay()
repaint()
check()
whitePlay()
repaint()
check()
...
面向?qū)ο蟮模浩灞P根盒、玩家
棋盤.開局 - 棋手.下棋 - 棋盤.重新展示 - 棋盤.檢查勝負(fù) - 棋手.下棋 -----
const checkerBoard = new CheckerBoard()
// 對象在新建的時候會自動去完成初始化钳幅,開局等操作
const wPlayer = new Player('white')
const bPlayer = new Player('black')
wPlayer.play()
bPlayer.play()
// 棋手在操作 play 之后,棋盤可能會監(jiān)聽到相應(yīng)的事件炎滞,并自動去重新展示敢艰、檢查勝負(fù)等,無需手動操作册赛。這里再次可以看出面向?qū)ο笾行氖窃诿枋鏊婕暗降膶ο蟮男袨槟频迹皇菫榱四骋粋€目的而去做的操作震嫉,更符合事物的特點。
面向?qū)ο蟮奶匦栽谶@個例子中的表現(xiàn)
封裝:Player, CheckerBoard 類的使用者只需要關(guān)注對象暴露的接口牡属,無需關(guān)心類的內(nèi)部實現(xiàn)票堵。
繼承
多態(tài):白方和黑方選手,下棋逮栅,白棋/黑棋悴势;白方勝利/黑方勝利
什么時候適合使用面向?qū)ο蟮木幊趟枷耄?/h3>
在比較復(fù)雜/參與方較多的問題,面向?qū)ο罂梢愿玫睾喕瘑栴}证芭,更方便維護(hù)和擴展瞳浦。
在簡單的問題面前担映,這兩種思想沒什么差別废士,甚至面向過程更方便一些。
JS 中的面向?qū)ο?/h3>
方法
屬性
Object Array Date Function RegExp
創(chuàng)建對象
- 普通方式(創(chuàng)建空對象蝇完,補上屬性和方法)官硝。無復(fù)用
const Player = new Object()
player.name = 'ss'
player.play = function () {
}
工廠模式。無法判斷實例的類型
function createPlayer(name) {
const Player = new Object()
player.name = name
player.play = function () {
}
return player
}
- 構(gòu)造函數(shù)/實例
function Player (name) {
this.name = name
this.start = function () {
}
}
const p1 = new Player('w')
const p2 = new Player('b')
- 原型
function Player(name) {
this.name = name
}
Player.prototype.start = function () {
}
將方法放到原型鏈上優(yōu)化了內(nèi)存占用
- 靜態(tài)屬性/方法 實例屬性/方法
原型及原型鏈
怎么找到 Player 的原型對象短蜕?怎么找到更上級的原型對象氢架?
function Player (name) {
this.name = name
}
Player.prototype.start = function () {
console.log('下棋')
}
const p1 = new Player('1')
const p2 = new Player('2')
console.log(Player.prototype)
// { start: [Function (anonymous)] }
console.log(p1.__proto__)
// { start: [Function (anonymous)] }
console.log(Player.prototype === p1.__proto__)
// true
console.log(Player.prototype.constructor === Player)
// true
實例.proto === 構(gòu)造函數(shù).prototype
console.log(Player.__proto__)
// {}
console.log(Player.__proto__ === Function.prototype)
// true
構(gòu)造函數(shù)的上游原型是一個對象
到現(xiàn)在為止我們并沒有一個直接的對象是指向某個原型鏈上游的對象的,這就是他們?yōu)槭裁纯雌饋砟敲瓷衩嘏竽В鋵嵕椭皇莻€普通的對象而已岖研。
new 關(guān)鍵字做了什么
- 創(chuàng)建一個空對象
- 把該對象的
__proto__
指向構(gòu)造函數(shù)的prototype
- 把構(gòu)造函數(shù)的
this
顯示指向新創(chuàng)建的對象并調(diào)用構(gòu)造函數(shù) - 返回值
- 如果構(gòu)造函數(shù)沒有顯示的返回值,返回
this
- 如果構(gòu)造函數(shù)的返回值是 number string boolean 這種基本數(shù)據(jù)類型警检,返回
this
- 如果構(gòu)造函數(shù)的返回值是一個對象孙援,那就返回這個對象
- 如果構(gòu)造函數(shù)沒有顯示的返回值,返回
function mockNew() {
const obj = new Object()
const constructor = Array.prototype.shift.call(arguments, 1)
obj.__proto__ = constructor.prototype
// const obj = Object.create(constructor.prototype)
const result = constructor.apply(obj, arguments)
return typeof result === 'object' ? result : obj
}
function Player(name) {
this.name = name
}
const p = mockNew(Player, 'ss')
console.log(p) // Player { name: 'ss' }
console.log(p instanceof Player) // true