面試敲門磚、進階墊腳石、設計有模式、代碼更合理
原始碼
-
第1章 基礎知識
- 1-1 Node.js 基礎知識
- 1-2 TypeScript 基礎知識
-
第2章 面向物件
- 2-2 什么是面向物件
- 2-3 面向物件-封裝
- 2-4 面向物件-抽象
- 2-5 面向物件-繼承
- 2-6 面向物件-多型
- 2-7 面向物件-總結
- 2-8 UML圖-介紹
- 2-9 UML類圖
-
第3章 設計原則
- 3-1 設計原則-介紹
- 3-2 設計原則-何為設計
-
3-3 設計原則-5大原則
- 單一責任原則
- 開放封閉原則
- 里氏替換原則
- 介面獨立原則
- 依賴倒置原則
- 設計原則總結
- 用Promise來說明 S-O
- 3-4 用promise演示
- 3-5 設計模式簡介
-
3-6 23種設計模式介紹
- 創建型
- 結構型
- 行為型
- 如何講解設計模式
- 3-7 面試真題1
- 3-8 面試真題2
- 3-9 總結
-
第4章 工廠模式
- React.createElement
- vue異步組件
- 設計原則驗證
-
第5章 單例模式
- 示例
- 設計 原則 驗證
- 第6章 配接器模式
- 第7章 裝飾器模式
-
第8章 代理模式
- 8-1 代理模式-介紹和演示
- 8-2 代理模式-場景1(事件代理和jq的proxy)
- 8-3 代理模式-場景2(明星經紀人)
- 8-4 代理&配接器&裝飾模式對比
-
第9章 外觀模式
- 9-1 外觀模式
-
第10章 觀察者模式
- 10-1 觀察者模式-介紹和演示
- 10-2 觀察者模式-場景1jquery
- 10-3 觀察者模式-場景2NodeJs自定義事件
- 10-4 觀察者模式-其它場景
-
第11章 迭代器模式
- 11-1 迭代器模式-介紹
- 11-2 迭代器模式-演示
- 11-3 迭代器模式-場景1(ES6 Iterator)
- 11-4 迭代器模式-場景2
- 11-5 迭代器模式-代碼演示和總結
-
第12章 狀態模式
- 12-1 狀態模式-介紹和演示
- 12-2 狀態模式-場景1(有限狀態機)
- 12-3 狀態模式-場景2(寫一個promise)
-
第13章 其他設計模式
- 13-1 其他設計模式概述
- 13-2 原型模式
- 13-3 橋接模式
- 13-4 組合模式
- 13-5 享元模式
- 13-6 策略模式
- 13-7 模板方法模式和職責連模式
- 13-8 命令模式
- 13-9 備忘錄模式
- 13-10 中介者模式
- 13-11 訪問者模式和解釋器模式
- 第 14 章 推薦
第1章 基礎知識
1-1 Node.js 基礎知識
- Node.js介紹
- Node.js 是一個基于 Chrome V8 引擎的 JavaScript 運行時,
- Node.js安裝
- 常規Node.js 安裝配置
- 版本管理:Node.js版本管理工具nvm
- 版本管理:阿里Node.js版本管理工具
- Node.js 入門
- 七天學會NodeJS
1-2 TypeScript 基礎知識
- TS 入門
- TypeScript 教程
第2章 面向物件
2-2 什么是面向物件
- 1. 什么是面向物件編程?
- 面向物件編程是一種編程范式或編程風格,它以類或物件作為組織代碼的基本單元,并將封裝、抽象、繼承、多型四個特性,作為代碼設計和實作的基石 ,
- 2. 什么是面向物件編程語言?
- 支持類或物件的語法機制,并有現成的語法機制,能方便地實作面向物件編程四大特性(封裝、抽象、繼承、多型)的編程語言
- 3. 如何判定一個編程語言是否是面向物件編程語言?
- 嚴格:支持類、物件、四大特性
- 寬泛:類、物件
- 4. 面向物件編程和面向物件編程語言之間有何關系?
- 面向物件編程不一定要用面向物件語言,使用面向物件語言寫出的代碼也不一定是面向物件語言的
- 5. 什么是面向物件分析和面向物件設計?
- 面向物件分析就是要搞清楚做什么,面向物件設計就是要搞清楚怎么做,
- 產出是類圖
2-3 面向物件-封裝
- 語法關鍵字:private、protected、public
- 意義:
- 保護資料不被隨意修改,提高代碼的可維護性
- 僅暴露有限的必要介面,提高類的易用性,
2-4 面向物件-抽象
- 講如何隱藏方法的具體實作
- 意義:
- 提高代碼的可擴展性、維護性,修改實作不需要改變定義,減少代碼的改動范圍
- 處理復雜系統的有效手段,能有效地過濾掉不必要關注的資訊,
2-5 面向物件-繼承
- 表示類之間的 is-a 關系
- 單繼承和多繼承
- 意義:解決代碼復用的問題,
2-6 面向物件-多型
- 多型是指子類可以替換父類
- 意義:提高代碼的擴展性和復用性
2-7 面向物件-總結
- 面向物件編程有哪些優勢?
- 更能應對這種復雜型別的程式開發
- 更加豐富的特性(封裝、抽象、繼承、多型)
- 易擴展、易復用、易維護,
- 更加人性化、更加高級、更加智能,
2-8 UML圖-介紹

UML 中分9種圖:
靜態模型圖: 描述系統的靜態結構
類圖:顯示系統中的類, 介面以及它們之間的關系.

物件圖:類圖的一個實體

組件圖:各組件之間的關系


部署圖:顯示軟體系統不同的組件將在何處物理地運行,以及它們將如何彼此通信

動態模型圖: 描述系統的行為
用例圖:從客戶的角度來描述系統功能

活動圖(流程圖):描述系統的活動, 判定點和分支等.

時序圖:描述物件之間訊息的傳遞時間順序

協作圖:表達物件間的互動程序及物件間的關聯關系

狀態圖:通過建立物件的生存周期模型來描述物件隨時間變化的動態行為

2-9 UML類圖
- 類圖
- 資料型別
+表示 public,–表示 private#表示 protected~表示 package
- 資料型別

- 介面的表示法(實線空心箭頭)

類圖 中主要包括 4 種關系:
- 泛化關系(繼承關系):實線空心箭頭

實作關系(類與介面之間的實作關系):實線空心箭頭|虛線空心箭頭


依賴關系:虛線的箭頭

關聯關系

聚合關系:空心菱形的實心線,菱形指向整體
- 是整體與部分的關系,且部分可以離開整體而單獨存在

- 是整體與部分的關系,且部分可以離開整體而單獨存在
組成關系:帶實心菱形的實線,菱形指向整體
- 是整體與部分的關系,但部分不能離開整體而單獨存在

- 是整體與部分的關系,但部分不能離開整體而單獨存在
- 泛化關系(繼承關系):實線空心箭頭
第3章 設計原則
3-1 設計原則-介紹
- 即按照哪一種思路或者標準來實作功能
- 功能相同,可以有不同的設計方案來實作
- 伴隨著需求的增加,設計的作用才能體現出來
3-2 設計原則-何為設計
設計準則:
- 1 小既是美
- 2 讓每個程式只做好一件事
- 3 快速建立原型
- 4 舍棄高效率而取可移植性
- 5采用純文本來存盤資料
- 6 充分利用軟體的杠桿效應(軟體復用)
- 7 使用shell腳本來提高杠桿效應和可移植性
- 8 避免強制性的用戶界面
- 9 讓每一個程式都稱為過濾器
小準則:
- 允許用戶定制環境
- 盡量使作業系統內核小而輕量化
- 使用小寫字母并盡量簡短
- 沉默是金
- 各部分之和大于整體
- 尋求 90% 的解決方案
源于:《UNIX/LINUX 設計思想》
3-3 設計原則-5大原則
S O L I D 五大設計原則
- S - 單一責任原則
- O - 開放封閉原則
- L - 里氏替換原則
- I - 介面獨立原則
- D - 依賴倒置原則
單一責任原則
- 一個程式只做好一件事
- 如果功能過于復雜就拆分,每個部分保持獨立
開放封閉原則
- 對擴展開發,對修改封閉
- 增加需求時,擴展新代碼,而非修改已有代碼
- 這個是軟體設計的終極目標
里氏替換原則
- 子類能覆寫父類
- 父類能出現的地方子類就能出現
- JS中使用較少(弱型別&繼承使用較少)
介面獨立原則
- 保持介面的單一獨立,避免出現 “胖介面”
- JS中沒有介面(typescript例外),使用較少
- 類似于單一職責原則,這里更關注介面
依賴倒置原則
- 面向介面編程,依賴于抽象而不依賴于具體
- 使用方只關注介面而不關注具體類的實作
- JS中使用較少
設計原則總結
- S O 體現較多,詳細介紹
- LID 體現較少,但是要了解其用意
用Promise來說明 S-O
function loadImg(src) {
var promise = new Promise(function(resolve, reject) {
var img = document.createElement('img')
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('圖片加載失敗')
}
img.src = https://www.cnblogs.com/xulonglong/p/src
})
return promise
}
var src = 'https://img.uj5u.com/2020/09/18/818311808025921.png'
var result = loadImg(src)
result.then(function (img) {
console.log('img.width', img.width)
return img
}).then(function (img) {
console.log('img.height', img.height)
}).catch(function (err) {
console.error(err)
})
- 單一職責原則:每個 then 中的邏輯只做好一件事
- 開放封閉原則:如果新增需求,擴展then
- 對擴展開發,對修改封閉
3-4 用promise演示
就是3-3的代碼
3-5 設計模式簡介
從設計到模式
體會什么是設計?設計是設計,模式是模式,兩者是分離的,
該如何學習設計模式?
- 明白每個設計的道理和用意
- 通過經典應用體會它的真正使用場景
- 自己編碼時多思考,盡量模仿
3-6 23種設計模式介紹
其實設計模式大致分為三種型別:
- 創建型
- 組合型
- 行為型
這23種設計模式分別分散在這三種型別中,
創建型
- 工廠模式(工廠方法模式、抽象工廠模式、建造者模式)
- 工廠模式是講怎么面向物件、怎么創建物件、怎么生成
- 單例模式
- 單例模式是講如果這個系統中只有一個指定物件,那么出現第二個時,該怎么辦
- 原型模式
- 原型模式是講如何通過一個現有的物件,用拷貝的方式來生成另一個新的物件
結構型
- 配接器模式
- 裝飾器模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
行為型
- 策略模式
- 模板方法模式
- ★觀察者模式★
- 迭代器模式
- 職責鏈模式
- 命令模式
- 備忘錄模式
- ★狀態模式★
- 訪問者模式
- 中介模式
- 解釋器模式
如何講解設計模式
- 介紹和舉例(生活中易理解的示例)
- 畫UML類圖寫demo代碼
- 結合經典應用場景,講解該設計模式如何被使用
3-7 面試真題1
- 打車時,可以打專車或者快車,任何車都有車牌號和名稱
- 決議:需設計公共父類(車牌號和名稱),父類下又有兩子類(專車和快車))
- 不同車價格不同,快車每公里1元,專車每公里2元
- 決議:子類里有不同的價格
- 行程開始時,顯示車輛資訊
- 行車和車有關系,但和專車還是快車沒關系,所以我們需要依賴抽象編程,所以行程只和車有關系,不和具體哪種車有關,也就是說無論什么車都有行車資訊
- 所以我們需要再建一個"行程"的類,這個類參考車的某個屬性,我們可以通過這個屬性得到車的資訊(車牌號、名稱、單價)
- 行程結束時,顯示打車金額(假定行程就5公里)
- “金額”屬于行程,買了一萬輛車丟著是沒有行程金額的
UML類圖

class Car {
constructor(number, name) {
this.number = number
this.name = name
}
}
class Kuaiche extends Car {
constructor(number, name) {
super(number, name)
this.Price = 1
}
}
class Zhuanche extends Car {
constructor(number, name) {
super(number, name)
this.Price = 2
}
}
class Trip {
constructor(car) {
this.car = car
}
start() {
console.log(`行程開始,名稱:${this.car.name},車牌號:${this.car.Price}`)
}
end() {
console.log(`行程結束,價格:${this.car.Price * 5}`)
}
}
let car = new Kuaiche('101', '捷達')
let trip = new Trip(car)
trip.start()
trip.end()
3-8 面試真題2
- 某停車場,分3層,每層100車位
- 決議:三個類,分別是停車場、每層、車位,三個class
- 每個車位都能監控到車輛的駛入和離開
- 決議:我們要給車位這個類定義一個方法或者屬性來監控車輛駛入和離開,這個監控的方法要改變車位這個類的一個狀態,車位空不空
- 車輛進入前,顯示每層的空余車位數量
- 決議:車輛進入前肯定面對的是停車場這個類,所以這個資訊要在停車場這個類中釋放出來,所以我們加一個方法,動態計算顯示每一層(每一層都是一個類的實體)空車位,所以層這個類里還得加顯示空車位的方法,最終由停車場這個類累加后顯示
- 車輛進入時,攝像頭可以識別車牌號和時間
- 決議:還得加攝像頭的class,這個class有方法能識別出車牌號和記錄駛入時間,也就是說攝像頭這個類,輸入的是車的實體,輸出車牌號和時間,這個車牌號和時間要讓停車場那個類里去存,所以停車場這個類還得加車輛串列的屬性
- 車輛出來時,出口顯示幕顯示車牌號和停車時長
- 決議:還得加顯示幕的類,通過顯示幕拿到車牌號和記錄的駛入時間,然后用當前時間減去這個事件就拿到了停車時長

// 車
class Car {
constructor(num) {
this.num = num
}
}
// 入口攝像頭
class Camera {
shot(car) {
return {
num: car.num,
inTime: Date.now()
}
}
}
// 出口顯示幕
class Screen {
show(car, inTime) {
console.log('車牌號', car.num)
console.log('停車時間', Date.now() - inTime)
}
}
// 停車場
class Park {
constructor(floors) {
this.floors = floors || []
this.camera = new Camera()
this.screen = new Screen()
this.carList = {}
}
in(car) {
// 獲取攝像頭的資訊:號碼 時間
const info = this.camera.shot(car)
// 停到某個車位
const i = parseInt(Math.random() * 100 % 100)
const place = this.floors[0].places[i]
place.in()
info.place = place
// 記錄資訊
this.carList[car.num] = info
}
out(car) {
// 獲取資訊
const info = this.carList[car.num]
const place = info.place
place.out()
// 顯示時間
this.screen.show(car, info.inTime)
// 洗掉資訊存盤
delete this.carList[car.num]
}
emptyNum() {
return this.floors.map(floor => {
return `${floor.index} 層還有 ${floor.emptyPlaceNum()} 個車位`
}).join('\n')
}
}
// 層
class Floor {
constructor(index, places) {
this.index = index
this.places = places || []
}
emptyPlaceNum() {
let num = 0
this.places.forEach(p => {
if (p.empty) {
num = num + 1
}
})
return num
}
}
// 車位
class Place {
constructor() {
this.empty = true
}
in() {
this.empty = false
}
out() {
this.empty = true
}
}
// 測驗代碼------------------------------
// 初始化停車場
const floors = []
for (let i = 0; i < 3; i++) {
const places = []
for (let j = 0; j < 100; j++) {
places[j] = new Place()
}
floors[i] = new Floor(i + 1, places)
}
const park = new Park(floors)
// 初始化車輛
const car1 = new Car('A1')
const car2 = new Car('A2')
const car3 = new Car('A3')
console.log('第一輛車進入')
console.log(park.emptyNum())
park.in(car1)
console.log('第二輛車進入')
console.log(park.emptyNum())
park.in(car2)
console.log('第一輛車離開')
park.out(car1)
console.log('第二輛車離開')
park.out(car2)
console.log('第三輛車進入')
console.log(park.emptyNum())
park.in(car3)
console.log('第三輛車離開')
park.out(car3)
3-9 總結
第4章 工廠模式
原理
- 將
new操作單獨封裝 - 遇到
new時,就要考慮是否該使用工廠模式
示例
- 你去購買漢堡,直接點餐、取餐,不會自己親手做
- 商店要 “封裝” 做漢堡的作業,做好直接給顧客
/**
* 工廠模式示例,邏輯如圖:
*
* -------------------------- ----------------|
* | Creator | | Product |
* |------------------------| |---------------|
* | | | + name:String |
* |------------------------| -> |---------------|
* | + create(name):Product | | + init() |
* -------------------------- | + fn1() |
* | + fn2() |
* ----------------|
*/
class Product {
constructor(name) {
this.name = name;
}
init() {
console.log("init", this.name);
}
fn1() {
console.log("fn1", this.name);
}
fn2() {
console.log("fn2", this.name);
}
}
class Creator {
create(name) {
return new Product(name);
}
}
// 測驗
const creator = new Creator();
const p1 = creator.create("test1");
const p2 = creator.create("test2");
p1.init();
p2.init();
p1.fn1();
p2.fn1();
p1.fn2();
p2.fn2();
場景
jQuery - $('div')React.createElementvue異步組件
React.createElement

React.createElement使用工廠模式的好處:如果我們不用 createElement 封裝 new VNode(tag,attrs, children),在對生成VNode示例時我們還是讓用戶去驗證各個屬性引數,顯示不合理,而且用了工廠模式后用戶根本不關系內部建構式怎么變化,
vue異步組件

Vue異步加載組件完成后創建組件的模式
使用工廠模式把工廠內部建構式與用戶隔離
設計原則驗證
- 建構式和創建者分離
- 符合開放封閉原則
第5章 單例模式
- 系統中僅被唯一使用的
- 一個類只有一個實體
/**
* 單例模式
*/
class SingleObject {
login() {
console.log("login...");
}
}
// 創建一個靜態自執行的方法
SingleObject.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new SingleObject();
}
return instance;
}
})()
// 測驗
let obj1 = SingleObject.getInstance();
obj1.login();
let obj2 = SingleObject.getInstance();
obj2.login();
console.log(obj1 === obj2);
示例
- 登錄框
class LoginForm() {
constructor() {
this.state = 'hide'
}
hide() {
if(this.state === 'hide'){
console.log('已經隱藏')
return
}
this.state == 'hide'
consoel.log('隱藏成功')
}
show() {
if(this.state === 'show'){
console.log('已經顯示')
return
}
this.state === 'show'
console.log('顯示成功')
}
}
LoginForm.instance = (function(){
let instance
return function(){
if(!instance){
instance = new LoginForm()
}
return instance
}
})()
let login1 = LoginForm.instance()
login1.hide()
let login2 = LoginForm.instance()
login2.hide()
- 購物車
- vuex和redux中的store


jQuery永遠只有一個
設計 原則 驗證
- 符合單一職責原則,只實體化唯一的物件
- 沒法具體開放封閉原則,但是絕對不違反開放封閉原則
第6章 配接器模式
- 就介面格式和使用者不兼容
- 中間加一個配接器介面


/**
* 配接器模式
*/
class Adapter {
specificRequest() {
return "舊的介面內容"
}
}
class Target {
constructor() {
this.adapter = new Adapter();
}
request() {
let info = this.adapter.specificRequest();
return `${info} - 處理... - 新的介面內容`;
}
}
// 測驗
let target = new Target();
const r = target.request();
console.log(r);
場景
- 封裝舊介面
- Vue的computed
設計原則驗證
- 將就借口和使用者進行分離
- 符合開放封閉原則
第7章 裝飾器模式
- 為物件添加新功能
- 不改變其原有的結構和功能
- 將現有物件和裝飾器進行分離,兩者獨立存在
/**
* 裝飾器模式
*/
class Circle {
draw() {
console.log("畫一個圓");
}
}
class Decorator {
constructor(circle) {
this.circle = circle;
}
draw() {
this.circle.draw();
this.setRedBorder(this.circle);
}
setRedBorder(circle) {
console.log("設定紅色邊框");
}
}
// 測驗
let c = new Circle();
c.draw();
let d = new Decorator(c);
d.draw();
第8章 代理模式
8-1 代理模式-介紹和演示
- 使用者無權訪問目標物件
- 中間加代理,通過代理做授權和控制
- 代理類與目標類分離,隔離開目標類和使用者
8-2 代理模式-場景1(事件代理和jq的proxy)
8-3 代理模式-場景2(明星經紀人)
/**
* 代理模式
*/
class ReadImg {
constructor(filename) {
this.filename = filename;
this.loadFromDisk();
}
loadFromDisk() {
console.log("從硬碟加載資料" + this.filename);
}
display() {
console.log("顯示資料" + this.filename);
}
}
class ProxyImg {
constructor(filename) {
this.realImg = new ReadImg(filename);
}
display() {
this.realImg.display();
}
}
// test
let proxyImg = new ProxyImg("1.png");
proxyImg.display();
// =================================
/**
* 使用ES6語法的Proxy類演示 代理模式的示例,明星 - 經紀人
*/
let star = {
name: "張xx",
age : 25,
phone: "138123456789"
}
let agent = new Proxy(star, {
get: function(target, key) {
if (key === "phone") {
return "agent phone: 13555555555";
}
else if (key === "price") {
return 150000;
}
return target[key];
},
set: function(target, key, val) {
if (key === "customPrice") {
if (val < 10000) {
throw new Error("價格太低");
} else {
target[key] = val;
return true;
}
}
}
})
// test
console.log(agent.name);
console.log(agent.phone);
console.log(agent.age);
console.log(agent.price);
agent.customPrice = 120000; // OK
console.log(agent.customPrice);
agent.customPrice = 1000; // Error
console.log(agent.customPrice);
8-4 代理&配接器&裝飾模式對比
- 代理模式VS配接器模式
- 配接器模式:提供一個不同的介面(如不同版本的插頭)
- 代理模式:提供一模一樣的介面
- 代理模式VS裝飾器模式
- 裝飾器模式:擴展功能,原有功能不變且可直接使用
- 代理模式:顯示原有的功能,但是經過限制或者閹割之后的
第9章 外觀模式
9-1 外觀模式
- 為子系統中的一組介面提供了一個高層介面
- 使用者使用高層介面
- 不符合單一職責原則和開放封閉原則,因此謹慎使用,不可濫用

第10章 觀察者模式
10-1 觀察者模式-介紹和演示
- 發布 訂閱
- 一對多
- 主題和觀察者分離,不是主動觸發而是被動監聽,兩者解耦
10-2 觀察者模式-場景1jquery
10-3 觀察者模式-場景2NodeJs自定義事件
/**
* 觀察者模式,使用nodejs的events模塊的示例
*/
const EventEmitter = require("events").EventEmitter;
// =========EventEmitter的基礎用法=============
const emitter1 = new EventEmitter();
// 監聽some事件
emitter1.on("some", info => {
console.log("fn1", info);
})
// 監聽some事件
emitter1.on("some", info => {
console.log("fn2", info);
})
// 觸發some事件
emitter1.emit("some", "xxxx");
// =============================================
// 下面使用繼承來實作EventEmitter
class Dog extends EventEmitter {
constructor(name) {
super();
this.name = name;
}
}
let dog = new Dog("dog");
dog.on("bark", function() {
console.log(this.name, " barked-1");
})
dog.on("bark", function() {
console.log(this.name, " barked-2");
})
setInterval(() => {
dog.emit("bark")
}, 1000);
10-4 觀察者模式-其它場景
/**
* 觀察者模式
*/
// 主題,保存狀態,狀態變化之后觸發所有觀察者物件
class Subject {
constructor() {
this.state = 0;
this.observers = [];
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
this.notifyAllObservers();
}
notifyAllObservers() {
this.observers.forEach(observer => {
observer.update();
})
}
attach(observer) {
this.observers.push(observer);
}
}
// 觀察者
class Observer {
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update() {
console.log(`${this.name} update! state is: ${this.subject.state}`);
}
}
// 測驗
let s = new Subject();
let o1 = new Observer("o1", s);
let o2 = new Observer("o2", s);
let o3 = new Observer("o3", s);
let o4 = new Observer("o4", s);
s.setState(1);
s.setState(2);
第11章 迭代器模式
11-1 迭代器模式-介紹
- 順序訪問一個集合
- 使用者無需知道集合的內部結構(封裝)
- 迭代器物件與目標物件分離
- 迭代器將使用者與目標物件隔離開
11-2 迭代器模式-演示
11-3 迭代器模式-場景1(ES6 Iterator)
11-4 迭代器模式-場景2
11-5 迭代器模式-代碼演示和總結
/**
* 迭代器模式
*/
class Iterator {
constructor(container) {
this.list = container.list;
this.index = 0;
}
next() {
if (this.hasNext()) {
return this.list[this.index++];
}
return null;
}
hasNext() {
if (this.index >= this.list.length) {
return false;
}
return true;
}
}
class Container {
constructor(list) {
this.list = list;
}
// 生成遍歷器
getIterator() {
return new Iterator(this);
}
}
// 測驗
const arr = [1, 2, 3, 4, 5];
let container = new Container(arr);
let it = container.getIterator();
while(it.hasNext()) {
console.log(it.next());
}
// ============= 使用ES6的迭代器生成 =============
function each(data) {
// 生成遍歷器
let it = data[Symbol.iterator]();
let item;
do {
// 遍歷器生成可迭代內容,包含value和done屬性,
// 其中done屬性代替自定義的hasNext()方法,
// false表示還有資料,true則表示已經迭代完成
item = it.next();
if (!item.done) {
console.log(item.value);
}
} while (!item.done);
}
// ES6的Iterator已經封裝在了語法 for...of 中,直接使用即可
function each2(data) {
for (const item of data) {
console.log(item);
}
}
// 測驗
const arr2 = [10, 20, 30, 40, 50, 60];
let m = new Map();
m.set("a", 100);
m.set("b", 200);
m.set("c", 300);
each(arr2);
each(m);
each2(arr2);
each2(m);
第12章 狀態模式
12-1 狀態模式-介紹和演示
- 允許一個物件在其內部狀態改變的時候改變它的行為,物件看起來似乎修改了它的類
/**
* 狀態模式
*/
// 模擬紅綠燈狀態
class State {
constructor(color) {
this.color = color;
}
handle(context) {
console.log(`切換到 ${this.color} `);
context.setState(this);
}
}
// 主體
class Context {
constructor() {
this.state = null;
}
getState() {
return this.state;
}
setState(state) {
this.state = state;
}
}
// 測驗
let context = new Context();
let green = new State("綠燈");
let yellow = new State("黃燈");
let red = new State("紅燈");
// 綠燈亮
green.handle(context);
console.log(context.getState());
// 黃燈亮
yellow.handle(context);
console.log(context.getState());
// 紅燈亮
red.handle(context);
console.log(context.getState());
12-2 狀態模式-場景1(有限狀態機)
12-3 狀態模式-場景2(寫一個promise)
import * as fs from "fs";
import * as StateMachine from 'javascript-state-machine';
import * as request from 'request';
// promise state: resolve(pending => fullfilled), reject(pending => rejected)
const fsm = new StateMachine({
init: 'pending',
transitions: [
{
name: 'resolve',
form: 'pending',
to: 'fullfilled'
}, {
name: 'reject',
from: 'pending',
to: 'rejected'
}
],
methods: {
onResolve: function(state, data, data1) {
// sate 當前狀態機實體;data fsm.resolve(xxx) 傳遞的引數
// console.log(state, data)
data.succFnList.forEach(fn => fn(data1));
},
onReject: function(state, data) {
data.failFnList.forEach(fn => fn());
}
},
});
class MyPromise {
succFnList: any[];
failFnList: any[];
constructor(fn) {
this.succFnList = [];
this.failFnList = [];
fn((data) => {
// resolve 函式
fsm.resolve(this, data);
},
() => {
// reject 函式
fsm.reject(this);
});
}
then(succFn, failFn) {
this.succFnList.push(succFn);
this.failFnList.push(failFn);
}
}
function downloadImg(src) {
const promise = new MyPromise(function(resolve, reject) {
request(src, function(error, response, body) {
if (error) {
reject();
}
resolve(body);
})
});
return promise;
}
const imgSrc = https://www.cnblogs.com/xulonglong/p/'https://www.npmjs.com/package/javascript-state-machine';
const imgPromise = downloadImg(imgSrc);
imgPromise.then(function(data) {
console.log(fsm.state)
fs.writeFileSync('./test.html', data)
}, function(error) {
console.log(error);
});
第13章 其他設計模式
13-1 其他設計模式概述
13-2 原型模式
- 原型模式(prototype)是指用原型實體指向創建物件的種類,并且通過拷貝這些原型創建新的物件,
/**
* 原型模式
* prototype可以理解為ES6中class的一種底層原理,但是class是實作面向物件的基礎,并不是服務于某個模式
*/
// 創建一個原型
let prototype = {
getName: function() {
return this.first + " " + this.last;
},
say: function() {
console.log("Hello!");
}
}
// 基于原型創建x
let x = Object.create(prototype);
x.first = "A";
x.last = "B";
console.log(x.getName());
x.say();
// 基于原型創建y
let y = Object.create(prototype);
y.first = "C";
y.last = "D";
console.log(y.getName());
y.say();
13-3 橋接模式
- 橋接模式(Bridge)將抽象部分與它的實作部分分離,使它們都可以獨立地變化,
/**
* 橋接模式
*/
class Color {
constructor(name) {
this.name = name;
}
}
class Shape {
constructor(name, color) {
this.name = name;
this.color = color;
}
draw() {
console.log(`使用${this.color.name}顏色畫了一個${this.name}`);
}
}
// 測驗
let red = new Color("red");
let yellow = new Color("yellow");
let circle = new Shape("circle", red);
circle.draw();
let triangle = new Shape("triangle", yellow);
triangle.draw();
13-4 組合模式
- 將物件組合成樹形結構,以表示“整體-部分”的層次結構,
- 通過物件的多型表現,使得用戶對單個物件和組合物件的使用具有一致性,
class TrainOrder {
create () {
console.log('創建火車票訂單')
}
}
class HotelOrder {
create () {
console.log('創建酒店訂單')
}
}
class TotalOrder {
constructor () {
this.orderList = []
}
addOrder (order) {
this.orderList.push(order)
return this
}
create () {
this.orderList.forEach(item => {
item.create()
})
return this
}
}
// 可以在購票網站買車票同時也訂房間
let train = new TrainOrder()
let hotel = new HotelOrder()
let total = new TotalOrder()
total.addOrder(train).addOrder(hotel).create()
13-5 享元模式
- 運用共享技術有效地支持大量細粒度物件的復用,系統只使用少量的物件,而這些物件都很相似,狀態變化很小,可以實作物件的多次復用,由于享元模式要求能夠共享的物件必須是細粒度物件,因此它又稱為輕量級模式,它是一種物件結構型模式
let examCarNum = 0 // 駕考車總數
/* 駕考車物件 */
class ExamCar {
constructor(carType) {
examCarNum++
this.carId = examCarNum
this.carType = carType ? '手動檔' : '自動檔'
this.usingState = false // 是否正在使用
}
/* 在本車上考試 */
examine(candidateId) {
return new Promise((resolve => {
this.usingState = true
console.log(`考生- ${ candidateId } 開始在${ this.carType }駕考車- ${ this.carId } 上考試`)
setTimeout(() => {
this.usingState = false
console.log(`%c考生- ${ candidateId } 在${ this.carType }駕考車- ${ this.carId } 上考試完畢`, 'color:#f40')
resolve() // 0~2秒后考試完畢
}, Math.random() * 2000)
}))
}
}
/* 手動檔汽車物件池 */
ManualExamCarPool = {
_pool: [], // 駕考車物件池
_candidateQueue: [], // 考生佇列
/* 注冊考生 ID 串列 */
registCandidates(candidateList) {
candidateList.forEach(candidateId => this.registCandidate(candidateId))
},
/* 注冊手動檔考生 */
registCandidate(candidateId) {
const examCar = this.getManualExamCar() // 找一個未被占用的手動檔駕考車
if (examCar) {
examCar.examine(candidateId) // 開始考試,考完了讓佇列中的下一個考生開始考試
.then(() => {
const nextCandidateId = this._candidateQueue.length && this._candidateQueue.shift()
nextCandidateId && this.registCandidate(nextCandidateId)
})
} else this._candidateQueue.push(candidateId)
},
/* 注冊手動檔車 */
initManualExamCar(manualExamCarNum) {
for (let i = 1; i <= manualExamCarNum; i++) {
this._pool.push(new ExamCar(true))
}
},
/* 獲取狀態為未被占用的手動檔車 */
getManualExamCar() {
return this._pool.find(car => !car.usingState)
}
}
ManualExamCarPool.initManualExamCar(3) // 一共有3個駕考車
ManualExamCarPool.registCandidates([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) // 10個考生來考試
13-6 策略模式
- 定義一系列的演算法,把它們一個個封裝起來,并且使它們可以互相替換
/**
* 策略模式
*/
// 普通情況下,沒有使用策略模式
class User {
constructor(type) {
this.type = type;
}
buy() {
if (this.type === "ordinary") {
console.log("普通用戶購買");
} else if (this.type === "member") {
console.log("會員用戶購買");
} else if (this.type === "vip") {
console.log("高級會員購買");
}
}
}
// 使用
let u1 = new User("ordinary");
u1.buy();
let u2 = new User("member");
u2.buy();
let u3 = new User("vip");
u3.buy();
// ================ 使用策略模式進行調整 ===================
class OrdinaryUser {
buy() {
console.log("普通用戶購買");
}
}
class MemberUser {
buy() {
console.log("會員用戶購買");
}
}
class VipUser {
buy() {
console.log("高級會員用戶購買");
}
}
// 測驗
let ou = new OrdinaryUser();
ou.buy();
let mu = new MemberUser();
mu.buy();
let vu = new VipUser();
vu.buy();
13-7 模板方法模式和職責連模式
/**
* 職責鏈模式
*/
class Action {
constructor(name) {
this.name = name;
this.nextAction = null;
}
setNextAction(action) {
this.nextAction = action;
}
handle() {
console.log(`${this.name} 執行了操作`);
if (this.nextAction) {
this.nextAction.handle();
}
}
}
// 測驗
let a1 = new Action("組長");
let a2 = new Action("經理");
let a3 = new Action("總監");
a1.setNextAction(a2);
a2.setNextAction(a3);
a1.handle();
13-8 命令模式
- 將一個請求封裝成一個物件,從而讓你使用不同的請求把客戶端引數化,對請求排隊或者記錄請求日志,可以提供命令的撤銷和恢復功能,
/**
* 命令模式
*/
class Receiver {
exec() {
console.log("執行");
}
}
class Command {
constructor(receiver) {
this.receiver = receiver;
}
cmd() {
console.log("觸發命令");
this.receiver.exec();
}
}
class Invoker {
constructor(command) {
this.command = command;
}
invoke() {
console.log("開始");
this.command.cmd();
}
}
// 測驗
let soldier = new Receiver();
let trumpeter = new Command(soldier);
let general = new Invoker(trumpeter);
general.invoke();
13-9 備忘錄模式
- 在不破壞封裝性的前提下,捕獲一個物件的內部狀態,并在該物件之外保存這個狀態,這樣以后就可將該物件恢復到保存的狀態,
/**
* 備忘錄模式
*/
// 備忘類
class Memento {
constructor(content) {
this.content = content;
}
getContent() {
return this.content;
}
}
// 備忘串列
class CareTaker {
constructor() {
this.list = [];
}
add(memento) {
this.list.push(memento);
}
get(index) {
return this.list[index];
}
}
// 編輯器
class Editor {
constructor() {
this.content = null;
}
setContent(content) {
this.content = content;
}
getContent() {
return this.content;
}
saveContentToMemento() {
return new Memento(this.content);
}
getContentFromMemento(memento) {
this.content = memento.getContent();
}
}
// 測驗
let editor = new Editor();
let careTaker = new CareTaker();
editor.setContent("111");
editor.setContent("222");
careTaker.add(editor.saveContentToMemento()); // 備份
editor.setContent("333");
careTaker.add(editor.saveContentToMemento()); // 備份
editor.setContent("444");
console.log(editor.getContent());
editor.getContentFromMemento(careTaker.get(1)); // 撤銷
console.log(editor.getContent());
editor.getContentFromMemento(careTaker.get(0)); // 撤銷
console.log(editor.getContent());
13-10 中介者模式
- 解除物件與物件之間的緊耦合關系,增加一個中介者物件后,所有的 相關物件都通過中介者物件來通信,而不是互相參考,所以當一個物件發生改變時,只需要通知 中介者物件即可,中介者使各物件之間耦合松散,而且可以獨立地改變它們之間的互動,中介者
模式使網狀的多對多關系變成了相對簡單的一對多關系(類似于觀察者模式,但是單向的,由中介者統一管理,)
/**
* 中介者模式
*/
class A {
constructor() {
this.number = 0;
}
setNumber(num, m) {
this.number = num;
if (m) {
m.setB();
}
}
}
class B {
constructor() {
this.number = 0;
}
setNumber(num, m) {
this.number = num;
if (m) {
m.setA();
}
}
}
class Mediator {
constructor(a, b) {
this.a = a;
this.b = b;
}
setA() {
let number = this.b.number;
this.a.setNumber(number / 100);
}
setB() {
let number = this.a.number;
this.b.setNumber(number * 100);
}
}
// 測驗
let a = new A();
let b = new B();
let m = new Mediator(a, b);
a.setNumber(100, m);
console.log(a.number, b.number);
b.setNumber(100, m);
console.log(a.number, b.number);
13-11 訪問者模式和解釋器模式
- 訪問者模式: 給定一個語言, 定義它的文法的一種表示,并定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子,
class Context {
constructor() {
this._list = []; // 存放 終結符運算式
this._sum = 0; // 存放 非終結符運算式(運算結果)
}
get sum() {
return this._sum;
}
set sum(newValue) {
this._sum = newValue;
}
add(expression) {
this._list.push(expression);
}
get list() {
return [...this._list];
}
}
class PlusExpression {
interpret(context) {
if (!(context instanceof Context)) {
throw new Error("TypeError");
}
context.sum = ++context.sum;
}
}
class MinusExpression {
interpret(context) {
if (!(context instanceof Context)) {
throw new Error("TypeError");
}
context.sum = --context.sum;
}
}
/** 以下是測驗代碼 **/
const context = new Context();
// 依次添加: 加法 | 加法 | 減法 運算式
context.add(new PlusExpression());
context.add(new PlusExpression());
context.add(new MinusExpression());
// 依次執行: 加法 | 加法 | 減法 運算式
context.list.forEach(expression => expression.interpret(context));
console.log(context.sum);
第 14 章 推薦
- B站:UML教程
- 設計模式之美
- B站:JS 設計模式教程
- 《設計模式》
- 《重構 2》
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/72149.html
標籤:JavaScript
