手寫一下JavaScript的幾種設計模式

想分享的幾種設計模式

目前模式:工廠模式,單例模式,適配器模式,裝飾者模式,建造者模式javascript

建造者模式

簡介:建造者模式(builder pattern)比較簡單,它屬於建立型模式的一種。html

白話:4個部分:有個產品,有個工廠能夠造產品,有個設計師指揮造多少,有我的想買產品。vue

買產品的用戶不介意產品製造流程,只須要產品!java

function Cola() {
	this.sugar = '50g',
	this.water = '100g'
}
function Packing() { // 第一種打包方式
	this.createPkg = function(){
		console.log('建立可樂外皮')
	}
    this.pushCola = function() {
		console.log('可樂倒進瓶子')
	}
	this.complete = function() {
		var cola = new Cola()
		cola.complete = true
		return cola
	}
	this.init = function() {
	    this.createPkg() // 建立外皮
	    this.pushCola() // 倒進瓶子
	    //還能夠增長其餘步驟
	    return this.complete() // 製做完成
	}
}
function greenPacking() { //綠皮可樂打包方式
	this.createPkg = function(){
		console.log('建立green可樂外皮')
	}
    this.pushCola = function() {
		console.log('可樂倒進green瓶子')
	}
	this.complete = function() {
		var cola = new Cola()
		cola.complete = true
		return cola
	}
	this.init = function() {
	    this.createPkg() // 建立外皮
	    this.pushCola() // 倒進瓶子
	    //還能夠增長其餘步驟
	    return this.complete() // 製做完成
	}
}
function Boss() {
	this.createCola = function(packType) {
	    const pack = new window[packType]
	    this.product = pack.init() //完整產品產出
	}
	this.getCola = function(packType) {
		this.createCola(packType);
		return this.product
	}
}
const boss = new Boss()
var UserCola = boss.getCola('greenPacking') // UserCola.complete === true
複製代碼

其餘東西都不要,只要最後生產好的Cola,有sugar,有water。es6

關鍵在於Boss 函數中,擔任一個整合的職責typescript

一樣的Boss函數,我能夠經過更換Packing函數,打包方式,得到不一樣樣式的Cola。segmentfault

經過給getCola函數傳入不一樣想要的參數,得到不一樣的最終產品。實現了可插拔的函數結構。後端

裝飾者模式

裝飾者提供比繼承更有彈性的替代方案。 裝飾者用用於包裝同接口的對象,不只容許你向方法添加行爲,並且還能夠將方法設置成原始對象調用(例如裝飾者的構造函數)。設計模式

裝飾者用於經過重載方法的形式添加新功能,該模式能夠在被裝飾者前面或者後面加上本身的行爲以達到特定的目的。閉包

好處:

裝飾者是一種實現繼承的替代方案。當腳本運行時,在子類中增長行爲會影響原有類全部的實例,而裝飾者卻否則。取而代之的是它能給不一樣對象各自添加新行爲

function iwatch () {
	this.battery = 100;
	this.getBattery = function() {
		console.log(this.battery)
	}
}

iwatch.prototype.getNewPart = function(part) {
	this[part].prototype = this; //把this對象上的屬性 指向 新對象的prototype
	return new this[part]; //返回一個新對象,不修改原對象,新增了新對象的屬性
}

iwatch.prototype.addNetwork = function() {
	this.network = function() {
		console.log('network')
	}
}

iwatch.prototype.addSwim = function() {
	this.swim = function() {
		console.log('swim')
	}
}

var watch = new iwatch();
watch.getBattery(); // 100

watch = watch.getNewPart('addNetwork'); // 添加新行爲,network()
watch = watch.getNewPart('addSwim'); // 既有network方法,也有swim方法
複製代碼

具體例子: typescript裝飾器

在 ES7 中引入了@decorator 修飾器的提案,參考阮一峯的文章。

@testable
class MyTestableClass {
  // ...
}

function testable(target) {
  target.isTestable = true;
}

MyTestableClass.isTestable // true
複製代碼

直接可使用,裝飾器行爲

@decorator
class A {}

// 等同於

class A {}
A = decorator(A) || A;
複製代碼

更多的詳見:阮一峯博客

工廠模式

一個工廠能生產好多不一樣的產品,最多見的工廠函數就是jQ的$()函數,每個函數的結果都是一個須要的產品。

function Product(name) {
  this.name = name;
}
Product.prototype.init = function () {
  console.log('init');
}
Product.prototype.go = function () {
  console.log('go');
}

function Factory () {
}
Factory.prototype.add = function(name) {
  return new Product(name);
}

//use
let f = new Factory();
let a = f.add('a');

console.log(a.name);
a.init();
a.go();
複製代碼

適配器模式

Adapter,將一個類(對象)的接口(方法或者屬性)轉化爲另外一個接口,以知足用戶需求,使類(對象)之間接口的不兼容問題經過適配器得以解決

function Person () {
}
Person.prototype.Say = function() {
  throw new Error("該方法必須被重寫!")
}
Person.prototype.Walk = function() {
  throw new Error("該方法必須被重寫!")
}

function Dog () {
}
Dog.prototype.Walk = function() {
  throw new Error("該方法必須被重寫!")
}
Dog.prototype.shout = function() {
  throw new Error("該方法必須被重寫!")
}

function PersonA () {
  Person.apply(this)
}
PersonA.prototype = new Person()
PersonA.prototype.Say = function() {
  console.log('Person say')
}
PersonA.prototype.Walk = function() {
  console.log('Person Walk')
}

function DogBlack () {
  Dog.apply(this)
}
DogBlack.prototype = new Dog()
DogBlack.prototype.Walk = function() {
  console.log('Dog Walk')
}
DogBlack.prototype.shout = function() {
  console.log('Dog Shout')
}

//如今但願Dog類也能夠學會Say,而且多走幾步

function DogSayAdapter (DogClass) {
  Dog.apply(this)
  this.DogClass = DogClass
}
DogSayAdapter.prototype = new Dog()
DogSayAdapter.prototype.Say = function() {
  this.DogClass.shout()
}
DogSayAdapter.prototype.Walk = function() {
  this.DogClass.Walk()
  this.DogClass.Walk()
}

var personA = new PersonA()
var dogBlack = new DogBlack()
var dogSay = new DogSayAdapter(dogBlack)

personA.Say()
personA.Walk()
dogBlack.Walk()
dogBlack.shout()

dogSay.Say()
dogSay.Walk()//walk * 2 

複製代碼

適配器不僅是函數接口,還有數據格式的適配

在先後端數據傳遞時,經常使用到適配器模式,也就是通俗易懂的格式化數據,format函數等等

vue的computed計算屬性也是適配器模式的一種實現

const originData = [
	{
		title: 'title',
		age: 18,
		content: ['123',321],
		callback: function(){
			console.log(this)
		}
	},
  {
		title: 'title2',
		age: 1,
		content: ['1',3],
		callback: function(){
			console.log('title2')
		}
	}
]

function dataAdapter(data) {
  return data.map(item => {
    return {
      title: item.title,
      content: item.content.join(','),
      init: item.callback
    }
  })
}

var formatData = dataAdapter(originData)
複製代碼

e.g:原始data 的數據不知足當前的要求,經過適配器,把數據格式化成想要的格式,對原始數據沒有改變

單例模式

function Simple (name) {
  this.name = name
}
Simple.prototype.go = function() {
  this.name = 'go'
  console.log(this.name)
}

//static靜態方法
Simple.getInstance = (function() {
  var ins
  return function(name){
    if (!ins) {
      ins = new Simple(name)
    }
    return ins
  }
})()

let a = Simple.getInstance('a') // name: a
let b = Simple.getInstance('b') // name: a

b===a//true
複製代碼

非單例模式下,相同的new Simple()構造函數,不相等。

經過閉包只建立一次Simple實例,你們公用一個。

惰性單例模式

惰性和懶加載lazyload類似,延遲加載,或者說須要時再加載,否則一次加載過多,頻繁進行操做dom影響性能

儘管上述代碼有Simple.getInstance方法,能夠在須要時再進行實例化,但仍然不是一個好的實現方式。

能夠將惰性加載的部分提取出來。

e.g:

var simple = function(fn) {
    var instance;
    return function() {
        return instance || (instance = fn.apply(this, arguments));
    }
};
// 建立遮罩層
var createMask = function(){
    // 建立div元素
    var mask = document.createElement('div');
    // 設置樣式
    mask.style.position = 'fixed';
    mask.style.top = '0';
  	...
    ...
    document.body.appendChild(mask);
    // 單擊隱藏遮罩層
    mask.onclick = function(){
        this.style.display = 'none';
    }
    return mask;
};

// 建立登錄窗口
var createLogin = function() {
    // 建立div元素
    var login = document.createElement('div');
    // 設置樣式
    login.style.position = 'fixed';
    login.style.top = '50%';
  	...
    ...
    login.innerHTML = 'login it';
    document.body.appendChild(login);

    return login;
};

document.getElementById('btn').onclick = function() {
    var oMask = simple(createMask)();
    oMask.style.display = 'block';
    var oLogin = simple(createLogin)();
    oLogin.style.display = 'block';
}
複製代碼

參考資料

segmentfault.com/a/119000001…

juejin.im/post/5e021e…

c.biancheng.net/view/1317.h…

juejin.im/post/5b55db…

segmentfault.com/a/119000001…

www.cnblogs.com/TomXu/tag/%…

總結

對五種常見經常使用的設計模式進行了學習,這幾種不少時候都會用到,接下來還會繼續學習其餘的18種設計模式,可能有的設計模式不必定在實際敲碼中使用,學了沒壞處,總能用得上嗷!

網上對於設計模式的文章,書籍層出不盡,但看得再多,不如本身理解,而且實際使用。不少時候是幾種設計模式融合在一塊兒使用,若是不是本身去寫一遍,理解一遍,可能常見的設計模式都理解不了。這樣就太惋惜了,發現乾淨整潔的代碼,都說不出哪裏好,就是看着舒服,順眼,運行速度快...

相關文章
相關標籤/搜索