前幾天我曾分享過幾張不那麼嚴謹的思惟導圖,其中便有關於設計模式的一張:javascript
在很長的一段時間裏,我只能記住某幾種設計模式,並無很好的應用。索性咱們就以該圖爲大綱,講講那些咱們不經意間使用的設計模式 --- 建立型。html
Factory Pattern
Simple Factory
)Factory method
)Abstract factory
)其核心就是:前端
工廠起到的做用就是隱藏了建立實例的複雜度,只須要提供一個接口,簡單清晰。 --- 摘自《前端面試之道》vue
而區別則是:java
其實從你會用jQuery
開始,就已經在用工廠模式了:node
jQuery
的$(selector)
jQuery
中$('div')
和new $('div')
哪一個好用?很顯然直接$()
最方便 ,這是由於$()
已是一個工廠方法了。算法
class jQuery {
constructor(selector) {
super(selector)
}
// ....
}
window.$ = function(selector) {
return new jQuery(selector)
}
複製代碼
React
的createElement()
React.createElement()
方法就是一個工廠方法vue-cli
React.createElement('h1', null, 'Hello World!'),
複製代碼
Vue
的異步組件經過promise
的方式resolve
出來一個組件 後端
export function createComponent (
Ctor: Class<Component> | Function | Object | void,
data: ?VNodeData,
context: Component,
children: ?Array<VNode>,
tag?: string
): VNode | Array<VNode> | void {
// ...邏輯處理
// async component
let asyncFactory
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
}
複製代碼
Singleton Pattern
實例一次後到處可用
單例模式的要點有三個:
從具體實現角度來講,就是如下三點:
一樣的,它也是咱們最先接觸的一種設計模式:
屢次引用只會使用一個庫引用,如 jQuery
,lodash
,moment
等。
Vuex / Redux
全局狀態管理store
Vuex
和Redux
數據保存在單一store
中,Mobx
將數據保存在分散的多個store
中
const store = createStore(reducer)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
複製代碼
Vue
中第三方插件的安裝首當其衝的就是Vuex
安裝:
let Vue // bind on install
export function install (_Vue) {
if (Vue && _Vue === Vue) {
// 若是發現 Vue 有值,就不從新建立實例了
return
}
Vue = _Vue
applyMixin(Vue)
}
複製代碼
其它也相似,感興趣的能夠去GitHub
搜索export function install (Vue)
class SingletonClass {
constructor() {
if (!SingletonClass.instance) {
SingletonClass.instance = this;
}
return SingletonClass.instance;
}
// other things
}
const instance = new SingletonClass();
Object.freeze(instance);
export default instance;
複製代碼
Builder Pattern
、建造者模式主要用於 「分步驟構建一個複雜的對象」
這在其中「分步驟」是一個穩定的算法,而複雜對象的各個部分則常常變化。
一句話:指揮者分配任務,建造者進行開發,各執其責,穩定在一個大的流程裏面去。
一位女士要建造一座別墅,須要找來一位包工頭,包工頭再將具體的任務分配給工人作,作完以後再給女士使用。建造者模式概念擬物化解讀
jQuery
中的建造者jQuery
中建造者模式體如今:
$( "<div class= "foo">bar</div>" );
$( "<p id="test">foo <em>bar</em></p>").appendTo("body" );
var newParagraph = $( "<p />" ).text( "Hello world" );
$( "<input />" )
.attr({ "type": "text", "id":"sample"});
.appendTo("#container");
複製代碼
下面是jQuery
源碼內部jQuery.prototype
方法的一個片斷,它將從傳遞給jQuery()
選擇器的標記構建jQuery
對象。
不管是否 document.createElement
用於建立新元素,對元素(找到或建立)的引用都會注入到返回的對象中,所以.attr()
能夠在其後當即使用其餘方法。
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
context = context instanceof jQuery ? context[0] : context;
doc = ( context ? context.ownerDocument || context : document );
//若是傳入的是單個字符串,而且是單個標記
//只需執行createElement並跳過其他部分
ret = rsingleTag.exec( selector );
if ( ret ) {
if ( jQuery.isPlainObject( context ) ) {
selector = [ document.createElement( ret[1] ) ];
jQuery.fn.attr.call( selector, context, true );
} else {
selector = [ doc.createElement( ret[1] ) ];
}
} else {
ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
selector = ( ret.cacheable ? jQuery.clone(ret.fragment)
: ret.fragment ).childNodes;
}
return jQuery.merge( this, selector );
複製代碼
本質上,建造者模式的目標是減小構造函數所用的參數數量,並提供向對象添加靈活的行爲方法。
// 使用建造者模式以前
const person1 = new Person('Peter', 26, true, 40074986, 4, 2);
// 使用建造者模式以後
const person1 = new Person();
person1
.name('Peter')
.age(26)
.member(true)
.phone(40074986)
.children(4)
.cars(2);
複製代碼
ES6
中的建造者模式咱們來假設一個商品錄入系統的業務場景,有四個必填信息,分別是:名稱,價格,分類。 該build
方法將返回最終的JavaScript
對象。
/書籍建造者類
class ProductBuilder {
constructor() {
this.name = '';
this.price = 0;
this.category = '';
}
withName(name) {
this.name = name
return this
}
withPrice(price) {
this.price = price
return this
}
withCategory(category) {
this.category = category
return this
}
build() {
return {
name: this.name,
price: this.price,
category: this.category,
}
}
}
console.log(
new ProductBuilder()
.withName('《哈利·波特》')
.withCategory('book')
.withPrice('29.9')
.build()
複製代碼
雖然只有三個屬性,咱們的構建器已經至關大,而且須要不少withers
。
構建器的大小隨字段數量線性增加。咱們有太多的withxxxx
方法。咱們其實能夠自動建立這類withxxxx
方法以簡化代碼。
class ProductBuilder {
constructor() {
this.name = ''
this.price = ''
this.category = 'other'
// 爲每一個屬性生成`wither`
Object.keys(this).forEach(key => {
const witherName = `with${key.substring(0, 1).toUpperCase()}${key.substring(1)}`
this[witherName] = value => {
this[key] = value
return this
}
})
}
build() {
// 獲取今生成器的全部非函數屬性的數組
const keysNoWithers = Object.keys(this).filter(key => typeof this[key] !== 'function')
return keysNoWithers.reduce((returnValue, key) => {
return {
...returnValue,
[key]: this[key],
}
}, {})
}
}
console.log(
new ProductBuilder()
.withName('《哈利波特》')
.withCategory('book')
.build()
)
複製代碼
咱們將全部的建造方法withxxxx在constructor調用時自動被建立,這裏咱們使用了一些ES6的新語法:Object.keys獲取對象屬性數組,...的合併對象的語法
最終咱們獲得了一種聲明式(易於理解)的方法,且能夠動態添加屬性的建造者模式。
當你有許多建造者時,咱們能夠輕鬆地將其廣義部分提取到一個通用的父類中,從而能夠很是輕鬆地建立新的建造者。
class BaseBuilder {
init() {
Object.keys(this).forEach((key) => {
const witherName = `with${key.substring(0,1).toUpperCase()}${key.substring(1)}`;
this[witherName] = (value) => {
this[key] = value;
return this;
};
});
}
build() {
const keysNoWithers = Object.keys(this).filter((key) => (
typeof this[key] !== 'function'
));
return keysNoWithers.reduce((returnValue, key) => {
return {
...returnValue,
[key]: this[key]
};
}, {});
}
}
複製代碼
而後就能夠建立多個建造者了:
class ProductBuilder extends BaseBuilder {
constructor() {
super();
this.name = '《前端勸退祕訣》';
this.price = 9.99;
this.category = 'other';
super.init();
}
}
複製代碼
能夠看出,建造者模式的使用有且只適合建立極爲複雜的對象。在前端的實際業務中,在沒有這類極爲複雜的對象的建立時,仍是應該直接使用對象字面或工廠模式等方式建立對象。
prototype
...再講會被砍死吧。
本來打算又雙叒憋它個一萬多字,把全部設計模式寫個遍。
可是以爲吧,這樣閱讀體驗其實並很差(主要仍是懶,想慢慢寫。)
噢對了,如今還有靠譜內推的能夠聯繫我
huab119
454274033@qq.com
須要轉載到公衆號的喊我加下白名單就好了。