[js高手之路]設(shè)計(jì)模式系列課程-發(fā)布者,訂閱者重構(gòu)購物車的實(shí)例
發(fā)布者訂閱者模式,是一種很常見的模式,比如:
一、買賣房子
生活中的買房,賣房,中介就構(gòu)成了一個(gè)發(fā)布訂閱者模式,買房的人,一般需要的是房源,價(jià)格,使用面積等信息,他充當(dāng)了訂閱者的角色
中介拿到賣主的房源信息,根據(jù)手頭上掌握的客戶聯(lián)系信息(買房的人的手機(jī)號),通知買房的人,他充當(dāng)了發(fā)布者的角色
賣主想賣掉自己的房子,就需要告訴中介,把信息交給中介發(fā)布
二,網(wǎng)站訂閱信息的用戶
訂閱者角色:需要訂閱某類信息的網(wǎng)民,如某個(gè)網(wǎng)站的javascript類型文章
發(fā)布者角色:郵箱服務(wù)器,根據(jù)網(wǎng)站收集到的用戶訂閱郵箱,通知用戶.
網(wǎng)站主想把信息告訴訂閱者,需要把文章相關(guān)內(nèi)容告訴郵箱服務(wù)器去發(fā)送
等等非常多的例子,不一一列舉
本文用網(wǎng)站訂閱的方式,推導(dǎo)發(fā)布者-訂閱者框架,然后用發(fā)布者-訂閱者框架來重構(gòu)一個(gè)簡單的購物車
var Site = {};
Site.userList = [];
Site.subscribe = function( fn ){
this.userList.push( fn );
}
Site.publish = function(){
for( var i = 0, len = this.userList.length; i < len; i++ ){
this.userList[i].apply( this, arguments );
}
}
Site.subscribe( function( type ){
console.log( "網(wǎng)站發(fā)布了" + type + "內(nèi)容" );
});
Site.subscribe( function( type ){
console.log( "網(wǎng)站發(fā)布了" + type + "內(nèi)容" );
});
Site.publish( 'javascript' );
Site.publish( 'html5' );
Site.userList就是用來保存訂閱者
Site.subscribe就是具體的訂閱者,把每一個(gè)訂閱者訂閱的具體信息保存在Site.userList
Site.publish就是發(fā)布者:根據(jù)保存的userList,一個(gè)個(gè)遍歷(通知),執(zhí)行里面的業(yè)務(wù)邏輯
但是這個(gè),發(fā)布訂閱者模式,有個(gè)問題,不能訂閱想要的類型,上例我加了2個(gè)訂閱者(第11行,第14行),只要網(wǎng)站發(fā)了信息,全部能收到,但是有些用戶可能只想收到j(luò)avascript或者h(yuǎn)tml5的,所以,接下來,我們需要繼續(xù)完善,希望能夠接收到具體的信息,不是某人訂閱的類型,就不接收
var Site = {};
Site.userList = {};
Site.subscribe = function (key, fn) {
if (!this.userList[key]) {
this.userList[key] = [];
}
this.userList[key].push(fn);
}
Site.publish = function () {
var key = Array.prototype.shift.apply(arguments),
fns = this.userList[key];
if ( !fns || fns.length === 0) {
console.log( '沒有人訂閱' + key + "這個(gè)分類的文章" );
return false;
}
for (var i = 0, len = fns.length; i < len; i++) {
fns[i].apply(this, arguments);
}
}
Site.subscribe( "javascript", function( title ){
console.log( title );
});
Site.subscribe( "es6", function( title ){
console.log( title );
});
Site.publish( "javascript", "[js高手之路]寄生組合式繼承的優(yōu)勢" );
Site.publish( "es6", "[js高手之路]es6系列教程 - var, let, const詳解" );
Site.publish( "html5", "html5新的語義化標(biāo)簽" );
輸出結(jié)果:
[js高手之路]寄生組合式繼承的優(yōu)勢
[js高手之路]es6系列教程 - var, let, const詳解
沒有人訂閱html5這個(gè)分類的文章
我們可以看到,只有訂閱了javascript類型文章的人,才能收到 ”寄生組合式繼承的優(yōu)勢” 這篇文章,發(fā)布html5類型的時(shí)候,沒有任何人會收到.
es6類型的,只有訂閱es6的人,才能收到
我們已經(jīng)有了一個(gè)基本的發(fā)布訂閱者框架,接下來,把他完善成一個(gè)框架,便于其他功能或者其他網(wǎng)站系統(tǒng)的相同功能可以重用他
var Event = {
userList : {},
subscribe : function (key, fn) {
if (!this.userList[key]) {
this.userList[key] = [];
}
this.userList[key].push(fn);
},
publish : function () {
var key = Array.prototype.shift.apply(arguments),
fns = this.userList[key];
if (!fns || fns.length === 0) {
console.log('沒有人訂閱' + key + "這個(gè)分類的文章");
return false;
}
for (var i = 0, len = fns.length; i < len; i++) {
fns[i].apply(this, arguments);
}
}
};
var extend = function( dstObj, srcObj ){
for( var key in srcObj ){
dstObj[key] = srcObj[key];
}
}
var Site = {};
extend( Site, Event );
Site.subscribe( "javascript", function( title ){
console.log( title );
});
Site.subscribe( "es6", function( title ){
console.log( title );
});
Site.publish( "javascript", "寄生組合式繼承的優(yōu)勢" );
Site.publish( "es6", "es6系列教程 - var, let, const詳解" );
Site.publish( "html5", "html5新的語義化標(biāo)簽" );
然后,我們來重構(gòu)一個(gè)購物車實(shí)例,沒有重構(gòu)之前,我的購物車用的是面向過程:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/cart.js"></script>
</head>
<body>
<div id="box">
<ul>
<li>
<input type="button" value="-">
<span class="num">0</span>
<input type="button" value="+">
<span>單價(jià):</span>
<span class="unit">15元;</span>
<span class="label">小計(jì):</span>
<span class="subtotal">0</span>元
</li>
<li>
<input type="button" value="-">
<span class="num">0</span>
<input type="button" value="+">
<span>單價(jià):</span>
<span class="unit">10元;</span>
<span class="label">小計(jì):</span>
<span class="subtotal">0</span>元
</li>
<li>
<input type="button" value="-">
<span class="num">0</span>
<input type="button" value="+">
<span>單價(jià):</span>
<span class="unit">5元;</span>
<span class="label">小計(jì):</span>
<span class="subtotal">0</span>元
</li>
<li>
<input type="button" value="-">
<span class="num">0</span>
<input type="button" value="+">
<span>單價(jià):</span>
<span class="unit">2元;</span>
<span class="label">小計(jì):</span>
<span class="subtotal">0</span>元
</li>
<li>
<input type="button" value="-">
<span class="num">0</span>
<input type="button" value="+">
<span>單價(jià):</span>
<span class="unit">1元;</span>
<span class="label">小計(jì):</span>
<span class="subtotal">0</span>元
</li>
</ul>
<div class="total-box">
商品一共
<span id="goods-num">0</span>
件;
一共花費(fèi)
<span id="total-price">0</span>
元;
其中最貴的商品單價(jià)是<span id="unit-price">0</span>元
</div>
</div>
</body>
</html>
cart.js文件:
function getByClass(cName, obj) {
var o = null;
if (arguments.length == 2) {
o = obj;
} else {
o = document;
}
var allNode = o.getElementsByTagName("*");
var aNode = [];
for( var i = 0 ; i < allNode.length; i++ ){
if( allNode[i].className == cName ){
aNode.push( allNode[i] );
}
}
return aNode;
}
function getSubTotal( unitPrice, goodsNum ){
return unitPrice * goodsNum;
}
function getSum(){ //計(jì)算總花費(fèi)
var aSubtotal = getByClass("subtotal");
var res = 0;
for( var i = 0; i < aSubtotal.length; i++ ){
res += parseInt(aSubtotal[i].innerHTML);
}
return res;
}
function compareUnit() { //比單價(jià),找出最高的單價(jià)
var aNum = getByClass( "num");
var aUnit = getByClass( "unit");
var temp = 0;
for( var i = 0; i < aNum.length; i++ ){
if( parseInt(aNum[i].innerHTML) != 0 ){
if( temp < parseInt(aUnit[i].innerHTML) ) {
temp = parseInt(aUnit[i].innerHTML);
}
}
}
return temp;
}
window.onload = function () {
var aInput = document.getElementsByTagName("input");
var total = 0;
var oGoodsNum = document.getElementById("goods-num");
var oTotalPrice = document.getElementById("total-price");
var oUnitPrice = document.getElementById("unit-price");
for (var i = 0; i < aInput.length; i++) {
if (i % 2 != 0) { //加號
aInput[i].onclick = function () {
//當(dāng)前加號所在行的數(shù)量
var aNum = getByClass( "num", this.parentNode );
var n = parseInt( aNum[0].innerHTML );
n++;
aNum[0].innerHTML = n;
//獲取單價(jià)
var aUnit = getByClass( "unit", this.parentNode );
var unitPrice = parseInt(aUnit[0].innerHTML);
var subtotal = getSubTotal( unitPrice, n );
var aSubtotal = getByClass( "subtotal", this.parentNode );
aSubtotal[0].innerHTML = subtotal;
total++; //商品總數(shù)
oGoodsNum.innerHTML = total;
oTotalPrice.innerHTML = getSum();
oUnitPrice.innerHTML = compareUnit();
}
}else {
aInput[i].onclick = function(){
var aNum = getByClass( "num", this.parentNode );
if ( parseInt( aNum[0].innerHTML ) != 0 ){
var n = parseInt( aNum[0].innerHTML );
n--;
aNum[0].innerHTML = n;
//獲取單價(jià)
var aUnit = getByClass( "unit", this.parentNode );
var unitPrice = parseInt(aUnit[0].innerHTML);
var subtotal = getSubTotal( unitPrice, n );
var aSubtotal = getByClass( "subtotal", this.parentNode );
aSubtotal[0].innerHTML = subtotal;
total--; //商品總數(shù)
oGoodsNum.innerHTML = total;
oTotalPrice.innerHTML = getSum();
oUnitPrice.innerHTML = compareUnit();
}
}
}
}
}
耦合度太高,可維護(hù)性很差.
重構(gòu)之后的購物車:
window.onload = function () {
var Event = {
userList: {},
subscribe: function (key, fn) {
if (!this.userList[key]) {
this.userList[key] = [];
}
this.userList[key].push(fn);
},
publish: function () {
var key = Array.prototype.shift.apply(arguments),
fns = this.userList[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, len = fns.length; i < len; i++) {
fns[i].apply(this, arguments);
}
}
};
(function(){
var aBtnMinus = document.querySelectorAll( "#box li>input:first-child"),
aBtnPlus = document.querySelectorAll( "#box li>input:nth-of-type(2)"),
curNum = 0, curUnitPrice = 0;
for( var i = 0, len = aBtnMinus.length; i < len; i++ ){
aBtnMinus[i].index = aBtnPlus[i].index = i;
aBtnMinus[i].onclick = function(){
(this.parentNode.children[1].innerHTML > 0) && Event.publish( "total-goods-num-minus" );
--this.parentNode.children[1].innerHTML < 0 && (this.parentNode.children[1].innerHTML = 0);
curUnitPrice = this.parentNode.children[4].innerHTML;
Event.publish( "minus-num" + this.index,
parseInt( curUnitPrice ),
parseInt( this.parentNode.children[1].innerHTML )
);
};
aBtnPlus[i].onclick = function(){
(this.parentNode.children[1].innerHTML >= 0) && Event.publish( "total-goods-num-plus" );
this.parentNode.children[1].innerHTML++;
curUnitPrice = this.parentNode.children[4].innerHTML;
Event.publish( "plus-num" + this.index,
parseInt( curUnitPrice ),
parseInt( this.parentNode.children[1].innerHTML )
);
}
}
})();
(function(){
var aSubtotal = document.querySelectorAll("#box .subtotal"),
oGoodsNum = document.querySelector("#goods-num"),
oTotalPrice = document.querySelector("#total-price");
Event.subscribe( 'total-goods-num-plus', function(){
++oGoodsNum.innerHTML;
});
Event.subscribe( 'total-goods-num-minus', function(){
--oGoodsNum.innerHTML;
});
for( let i = 0, len = aSubtotal.length; i < len; i++ ){
Event.subscribe( 'minus-num' + i, function( unitPrice, num ){
aSubtotal[i].innerHTML = unitPrice * num;
});
Event.subscribe( 'plus-num' + i, function( unitPrice, num ){
aSubtotal[i].innerHTML = unitPrice * num;
});
}
})();
console.log( Event.userList );
}
以上這篇[js高手之路]設(shè)計(jì)模式系列課程-發(fā)布者,訂閱者重構(gòu)購物車的實(shí)例就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- JavaScript設(shè)計(jì)模式之觀察者模式(發(fā)布者-訂閱者模式)
- JS模式之簡單的訂閱者和發(fā)布者模式完整實(shí)例
- JavaScript中發(fā)布/訂閱模式的簡單實(shí)例
- JavaScript事件發(fā)布/訂閱模式原理與用法分析
- JavaScript實(shí)現(xiàn)與使用發(fā)布/訂閱模式詳解
- js 發(fā)布訂閱模式的實(shí)例講解
- js實(shí)現(xiàn)的訂閱發(fā)布者模式簡單示例
- JavaScript設(shè)計(jì)模式之觀察者模式(發(fā)布訂閱模式)原理與實(shí)現(xiàn)方法示例
- js設(shè)計(jì)模式之代理模式及訂閱發(fā)布模式實(shí)例詳解
- js簡單粗暴的發(fā)布訂閱示例代碼
相關(guān)文章
Extjs4中tree的拖拽功能(可以兩棵樹之間拖拽) 簡單實(shí)例
這篇文章主要介紹了Extjs4中tree的拖拽功能簡單實(shí)例,有需要的朋友可以參考一下2013-12-12
JS實(shí)現(xiàn)的Select三級下拉菜單代碼
這篇文章主要介紹了JS實(shí)現(xiàn)的Select三級下拉菜單,涉及javascript動態(tài)創(chuàng)建下拉列表的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08
JavaScript常見內(nèi)置函數(shù)大全數(shù)組篇(附超詳細(xì)案例)
這篇文章主要給大家介紹了關(guān)于JavaScript常見內(nèi)置函數(shù)大全數(shù)組篇的相關(guān)資料,文內(nèi)附超詳細(xì)案例,JavaScript中的數(shù)組是一種非常常用的數(shù)據(jù)結(jié)構(gòu),其內(nèi)置了一些非常有用的函數(shù),需要的朋友可以參考下2023-11-11
Javascript實(shí)現(xiàn)前端簡單的路由實(shí)例
本文將使用javascript實(shí)現(xiàn)一個(gè)極其簡單的路由實(shí)例。WEB開發(fā)中路由概念并不陌生,我們接觸到的有前端路由和后端路由。后端路由在很多框架中是一個(gè)重要的模塊,同樣前端路由在單頁面應(yīng)用也很常見,它使得前端頁面體驗(yàn)更流暢。2016-09-09
JavaScript和Vue分別實(shí)現(xiàn)逐字彈出(打字機(jī))效果
這篇文章主要為大家詳細(xì)介紹了如何通過CSS、JavaScript和Vue分別實(shí)現(xiàn)逐字彈出(打字機(jī))效果,文中的示例代碼講解詳細(xì),需要的小伙伴可以參考一下2024-01-01
js+HTML5 canvas 實(shí)現(xiàn)簡單的加載條(進(jìn)度條)功能示例
這篇文章主要介紹了js+HTML5 canvas 實(shí)現(xiàn)簡單的加載條(進(jìn)度條)功能,涉及javascript使用時(shí)間函數(shù)與canvas繪圖結(jié)合實(shí)現(xiàn)進(jìn)度條的相關(guān)操作技巧,需要的朋友可以參考下2019-07-07
Ajax+FormData+javascript實(shí)現(xiàn)無刷新表單信息提交
在前端開發(fā)中ajax,formdata和js實(shí)現(xiàn)無刷新表單信息提交非常棒,接下來通過本文給大家介紹Ajax+FormData+javascript實(shí)現(xiàn)無刷新表單信息提交的相關(guān)資料,需要的朋友可以參考下2016-10-10

