我的部落格:github.com/ruizhengyun…
為什麼要寫 Javascript 設計模式小書
關於 Javascript 設計模式的文章與書有很多很多,我寫這小書主要記錄我的學習過程中的筆記和心得,便於自己檢視,當然也想分享給走在前端路上的小夥伴(如果能幫到你一二,那也是極好的)。
小書中的每篇的篇幅都不是很長(單篇知識肯定沒講透),只是盡所能使其簡單和讓自己整明白各個模式是怎麼一回事(如果也有讓你整明白,那就更好了)。來吧,
為什麼要學設計(模式)
- 3 年工作經驗,面試必考;
- 成為專案技術負責人,設計能力是必要基礎;
- 從寫好程式碼,到做好設計,設計模式是必經之路;
現實問題
- 網站資料針對 java 等後端語言比較多;
- 看懂概念,不知道怎麼用,看完就忘;
- 現在的js框架(react、vue等),都用了哪些設計模式;
搭建開發環境
準備工作
# 專案初始化
npm init -y
# 新建開發目錄src
mkdir src
# 安裝webpack
npm install webpack-cli webpack --save-dev
# 安裝babel
npm install babel-loader babel-core babel-preset-env html-webpack-plugin babel-plugin-transform-decorators-legacy -D
# 安裝開發服務環境
npm install webpack-dev-server -D
# 新建配置webpack
touch webpack.dev.config.js
複製程式碼
編寫 webpack.dev.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: './src/index.js',
output: {
path: __dirname,
filename: './release/bundle.js' // release 會自動建立
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html' // bundle.js 會自動注入
})
],
devServer: {
contentBase: path.join(__dirname, './release'), // 根目錄
open: true, // 自動開啟瀏覽器
port: 3000, // 埠
historyApiFallback: true
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /(node_modules)/,
loader: 'babel-loader'
}
]
}
}
複製程式碼
編輯 package.json
,在 script
新增 dev
任務
{
...,
"scripts": {
...,
"dev": "webpack --config ./webpack.dev.config.js --mode development"
},
...
}
複製程式碼
物件導向
為什麼使用物件導向
- 程式的執行離不開 順序、判斷、迴圈 操作,也就是將其結構化;
- 物件導向就是將零散的資料結構化;
- 對於計算機而言,結構化的才是最簡單的(松本行弘的程式世界);
- 程式設計應該是簡單&抽象,簡單的前提是抽象,抽象後才簡單;
關於抽象:抽取事物的共同特徵就是抽取事物的本質特徵,捨棄非本質的特徵。所以抽象的過程也是一個裁剪的過程。在抽象時,同與不同,決定於從什麼角度上來抽象。抽象的角度取決於分析問題的目的。
物件導向三要素
- 繼承:子類繼承父類(用的多);
- 封裝:資料的許可權和保密(將物件裡面的某些屬性和方法不想讓別人看見。ES6 尚不支援,可用 typescript 演示);
- 多型:同一介面不同實現,簡單來講就是父類定義一個介面,子類實現不同的功能;
繼承
// 0.0.1/01.js
// 類,即模板
// 類,即模板
class People {
constructor(name, age) {
this.name = name
this.age = age
}
getName() {
console.log(`名字: ${this.name}`)
}
getAge() {
console.log(`年齡: ${this.age}`)
}
}
// 建立例項
const zhang = new People('張三', 27);
zhang.getName();
zhang.getAge();
// 建立例項
const li = new People('李四', 22);
li.getName();
li.getAge();
// 子類繼承父類
class Student extends People {
constructor(name, age, id) {
super(name, age)
this.id = id
}
getId() {
console.log(`${this.name},年齡 ${this.age},學號 ${this.id}`)
}
}
// 建立例項
const wang = new Student('王五', 22, '001')
wang.getId();
複製程式碼
總結:
People
是父類,公共的,不僅僅服務於Student
;- 可將公共方法抽離出來,提高複用,減少冗餘(這是軟體設計最基礎和最高效的方式);
封裝
// 0.0.1/02.js
// 封裝 public-開放 protected-對子類開放 private-對自己開放
// 線上編譯地址 => http://www.typescriptlang.org/play/
// 父類
class People {
public name
age
protected weight // 受保護屬性,只有自己或子類可用
constructor(name, age) {
this.name = name
this.age = age
this.weight = 120
}
getName() {
console.log(`名字: ${this.name}`)
}
getAge() {
console.log(`年齡: ${this.age}`)
}
}
// 繼承
class Student extends People {
id
private girlFriend
constructor(name, age, id) {
super(name, age)
this.id = id
this.girlFriend = '趙雪'
}
getId() {
console.log(`${this.name},年齡 ${this.age},學號 ${this.id}`)
}
getWeight() {
console.log(`${this.weight}`)
}
}
const xm = new Student('小明', 24, '002');
xm.getWeight();
// console.log(xm.girlFriend);
複製程式碼
說明
public
完全開發;portected
對子類開放;private
對自己開放(ES6 尚不支援,可用 typescript 演示);
總結
- 較少耦合,不該外露的不外露
- 利於資料、介面的許可權管理
- ES6目前不支援,一般認為 _開頭的屬性是 private ,比如var _num = 20
多型
// 0.0.1/03.js
class People {
constructor(name, age) {
this.name = name
this.age = age
}
getName() {
console.log(`名字: ${this.name}`)
}
getAge() {
console.log(`年齡: ${this.age}`)
}
}
class A extends People {
constructor(name) {
super(name)
}
getName() {
console.log(`A名字: ${this.name}`)
}
}
class B extends People {
constructor(name) {
super(name)
}
getName() {
console.log(`B名字: ${this.name}`)
}
}
// 建立例項
const p1 = new People('p1')
p1.getName();
const a1 = new A('a1')
a1.getName();
// 建立例項
const b1 = new B('b1')
b1.getName();
複製程式碼
總結
- 保持子類的開放性和靈活性;
- 面向介面程式設計(不用管子類如何實現,就看父類有多少介面) ;
- js 應用極少;
- 需要結合 java 等語言的介面、重寫、過載等功能;
應用舉例
以 jQuery 為例
// 0.0.1/04.js
class jQuery {
constructor(selector) {
let slice = Array.prototype.slice;
let dom = slice.call(document.querySelectorAll(selector));
let len = dom ? dom.length : 0;
for (let i = 0; i < len; i++) {
this[i] = dom[i];
}
this.length = len
this.selector = selector || ''
}
append(node) {
// ....
}
addClass(name) {
// ....
}
html(data) {
// ....
}
// 省略多個 API
}
window.$ = function(selector) {
// 工廠模式
return new jQuery(selector);
}
const $li = $('li')
console.log($li);
console.log($li.addClass);
複製程式碼
UML類圖
UML,統一建模語言(Unified Modeling Language)。類圖描述的是一種靜態關係,在系統的整個生命週期都是有效的,是物件導向系統的建模中最常見的圖,展現了一組物件、介面、協作和它們之間的關係。關係是指泛化(繼承)和關聯(引用)。
畫圖工具(工欲上其事必先利其器)
- MS Office visio;
- processon(不是會員有數量限制);
- Gliffy Digrams(chrome 應用外掛,不過沒有數量限制);
舉例:一個簡單類
class People {
constructor(name, age) {
this.name = name
this.age = age
}
getName() {
console.log(`名字: ${this.name}`)
}
getAge() {
console.log(`年齡: ${this.age}`)
}
}
複製程式碼
舉例:繼承與引用
// 0.0.1/05.js
class House {
constructor(city) {
this.city = city;
}
showCity() {
console.log(`城市:${this.city}`)
}
}
class People {
constructor(name, house) {
this.name = name
this.house = house
}
getInfo() {
console.log(`我是${this.name},有房在【${this.house.city}】`)
}
}
class Student extends People {
constructor(name, house) {
super(name, house)
}
getInfo() {
console.log(`我是${this.name},一名學生,有房在【${this.house.city}】`)
}
}
class Engineer extends People {
constructor(name, house) {
super(name, house)
}
getInfo() {
console.log(`我是${this.name},一名工程師,有房在【${this.house.city}】`)
}
}
// 例項化
const h1 = new House('杭州');
const p1 = new People('張三', h1)
p1.getInfo();
const s1 = new Student('李四', h1)
s1.getInfo();
const e1 = new Engineer('王五', h1)
e1.getInfo();
複製程式碼