你好!!

專注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站制作服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)洪山免費做網(wǎng)站提供優(yōu)質的服務。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了超過千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設實現(xiàn)規(guī)模擴充和轉變。
書中也存在有偏差的情況···
//發(fā)布
$.publish("test",?"a",?"bds",?"csdf",?"dsdf");
改為
$.publish("test",?["a",?"bds",?"csdf",?"dsdf"]);
javascript語言是單線程機制。所謂單線程就是按次序執(zhí)行,執(zhí)行完一個任務再執(zhí)行下一個。
對于瀏覽器來說,也就是無法在渲染頁面的同時執(zhí)行代碼。
單線程機制的優(yōu)點在于實現(xiàn)起來較為簡單,運行環(huán)境相對簡單。缺點在于,如果中間有任務需要響應時間過長,經常會導致
頁面加載錯誤或者瀏覽器無響應的狀況。這就是所謂的“同步模式”,程序執(zhí)行順序與任務排列順序一致。對于瀏覽器來說,
同步模式效率較低,耗時長的任務都應該使用異步模式;而在服務器端,異步模式則是唯一的模式,如果采用同步模式個人認為
服務器很快就會出現(xiàn)12306在高峰期的表現(xiàn)。。。。
異步模式的四種方式:
1.回調函數(shù)callback
所謂回調函數(shù),就是將函數(shù)作為參數(shù)傳到需要回調的函數(shù)內部再執(zhí)行。
典型的例子就是發(fā)送ajax請求。例如:
$.ajax({
async: false,
cache: false,
dataType: 'json',
url: "url",
success: function(data) {
console.log('success');
},
error: function(data) {
console.log('error');
}
})
當發(fā)送ajax請求后,等待回應的過程不會堵塞程序運行,耗時的操作相當于延后執(zhí)行。
回調函數(shù)的優(yōu)點在于簡單,容易理解,但是可讀性較差,耦合度較高,不易于維護。
2.事件驅動
javascript可以稱之為是基于對象的語言,而基于對象的基本特征就是事件驅動(Event-Driven)。
事件驅動,指的是由鼠標和熱鍵的動作引發(fā)的一連串的程序操作。
例如,為頁面上的某個
$('#btn').onclick(function(){
console.log('click button');
});
綁定事件相當于在元素上進行監(jiān)聽,是否執(zhí)行注冊的事件代碼取決于事件是否發(fā)生。
優(yōu)點在于容易理解,一個元素上可以綁定多個事件,有利于實現(xiàn)模塊化;但是缺點在于稱為事件驅動的模型后,流程不清晰。
3.發(fā)布/訂閱
發(fā)布訂閱模式(publish-subscribe pattern)又稱為觀察者模式(Observer pattern)。
該模式中,有兩類對象:觀察者和目標對象。目標對象中存在著一份觀察者的列表,當目標對象
的狀態(tài)發(fā)生改變時,主動通知觀察者,從而建立一種發(fā)布/訂閱的關系。
jquery有相關的插件,在這不是重點不細說了。。。。回頭寫個實現(xiàn)貼上來
4.promise模式
promise對象是CommonJS工作組提供的一種規(guī)范,用于異步編程的統(tǒng)一接口。
promise對象通常實現(xiàn)一種then的方法,用來在注冊狀態(tài)發(fā)生改變時作為對應的回調函數(shù)。
promise模式在任何時刻都處于以下三種狀態(tài)之一:未完成(unfulfilled)、已完成(resolved)和拒絕(rejected)。以CommonJS
Promise/A
標準為例,promise對象上的then方法負責添加針對已完成和拒絕狀態(tài)下的處理函數(shù)。then方法會返回另一個promise對象,以便于形成promise管道,這種返回promise對象的方式能夠支持開發(fā)人員把異步操作串聯(lián)起來,如then(resolvedHandler,
rejectedHandler); 。resolvedHandler
回調函數(shù)在promise對象進入完成狀態(tài)時會觸發(fā),并傳遞結果;rejectedHandler函數(shù)會在拒絕狀態(tài)下調用。
Jquery在1.5的版本中引入了一個新的概念叫Deferred,就是CommonJS promise A標準的一種衍生。可以在jQuery中創(chuàng)建
$.Deferref的對象。同時也對發(fā)送ajax請求以及數(shù)據(jù)類型有了新的修改,參考JQuery API。
除了以上四種,javascript中還可以利用各種函數(shù)模擬異步方式,更有詭異的諸如用同步調用異步的case
只能用team里同事形容java和javascript的一句話作為結尾:
“寫java像在高速路上開車,寫javascript像在草原上開車”-------------以此來形容javascript這種無類型的語言有多自由
but,如果草原上都是坑。
用CSS可以做到,其實是一個DIV,里面包含一個input,submit或button
DIV背景或邊框就是那個框
input是透明的無背景、無邊框
button的背景就是那個訂閱圖標。
去研究一下他的源碼就明細了:
CSS首先要定義:*{margin:0;padding:0;}
form ******
div class="byemail"
input id="header-subscribe-email" type="text" value="" class="f-text" name="email" autocomplete="off" /
input type="hidden" value="20" name="cityid" /
input type="submit" class="commit" value="訂閱" /
/div
/form
感覺二者非常像,都是pub/sub機制,如何進行區(qū)分?分別在什么不同的場景中進行應用?
在Obsever模式中, 不存在封裝約束的單一對象。Observer 和 Subject 必須合作才能維持約束。
Communication(通訊)模式由觀察者和目標互聯(lián)的方式決定:單一目標通常有很多觀察者,有時一個目標的觀察者是另一個觀察者的目標
Mediator 和 Observer 都能促進松耦合,然后Mediator 模式通過限制對象嚴格通過Mediator 進行通信來實現(xiàn)這個個目的
Observer 模式創(chuàng)建觀察者對喜愛那個,觀察者對象向訂閱它們的對喜愛那個發(fā)布其感興趣的事件。
在GoF的原文中是這樣描述觀察者模式的:
One or more observers are interested in the state of a subject and register their interest with the subject by attaching themselves. When something changes in our subject that the observer may be interested in, a notify message is sent which calls the update method in each observer. When the observer is no longer interested in the subject's state, they can simply detach themselves.
具體應用場景是,當subject的某個動作需要引發(fā)一系列不同對象的動作(比如你是一個班長要去通知班里的某些人),與其一個一個的手動調用觸發(fā)的方法(私下里一個一個通知),不如維護一個列表(建一個群),這個列表存有你想要調用的對象方法(想要通知的人);之后每次做的觸發(fā)的時候只要輪詢這個列表就好了(群發(fā)),而不用關心這個列表里有誰,只用關心想讓誰加入讓誰退出
這個列表就叫做ObserverList,它有一些維護列表方法:
function ObserverList(){
this.observerList = [];
}
ObserverList.prototype.Add = function( obj ){};
ObserverList.prototype.Empty = function(){};
ObserverList.prototype.Count = function(){};
ObserverList.prototype.Get = function( index ){};
ObserverList.prototype.Insert = function( obj, index ){};
ObserverList.prototype.IndexOf = function( obj, startIndex ){};
ObserverList.prototype.RemoveAt = function( index ){};
而我們的subject只用關心兩件事:1.維護這個列表,2.發(fā)布事件
function Subject(){
this.observers = new ObserverList();
}
Subject.prototype.AddObserver = function( observer ){
this.observers.Add( observer );
};
Subject.prototype.RemoveObserver = function( observer ){
this.observers.RemoveAt( this.observers.IndexOf( observer, 0 ) );
};
Subject.prototype.Notify = function( context ){
var observerCount = this.observers.Count();
for(var i=0; i observerCount; i++){
this.observers.Get(i).Update( context );
// 在這里假設的是列表里的每個對象都有update方法,但個人覺得這個列表里也可以是不同對象的不同方法,只要能接受當前上下文作為參數(shù), 可以這樣執(zhí)行:
// subscription.callback.apply( subscription.context, args );
}
};
中介模式(Mediator Pattern)
讓我們假設這樣一個場景: 有一個Manager一聲令下,需要讓工人A和工人B開工,代碼可以是這樣的
Manager.start = function () {
A.work();
B.work();
}
其實還可以這么寫,新增一個中介模塊,這個模塊有存儲了Manager的常用命令比如start,stop,resume,每一個命令其實維護的也是一個列表,比如start的列表下存儲了所有員工的start方法:
Mediator["start"] = [
{
name: 'A',
callback: 'work'
},
{
name: 'B',
callback: 'workAgain'
},
]
所以Manager的方法可以重寫為
Manager.start = function () {
Mediator.publish('start') // publish 為觸發(fā)命令函數(shù),以此來觸發(fā)start命令下維護的所有回調函數(shù)
}
代碼細節(jié)就不展示了,主要體現(xiàn)這么一個機制,而如果某個員工要提交自己的work方法供老板調用的話,只要注冊一下就好了
2
Mediator.subscribe('C', function callback() {});
問題是新增加一個中介模塊的好處是什么?
1.低耦合!如果不是經理要讓員工開始工作,是董事長怎么辦,或者是部門主管怎么辦,難道都要這么寫
XXX.start = function () {
A.work()
B.work();
}
都要把A.work什么抄一遍?當然不是,只要給中介模塊發(fā)出命令就好了,
2.模塊之間不需要進行通信,只要負責廣播和監(jiān)聽事件就好了
3.在模塊化的javascript中,中介模塊能提高可維護性:是否啟動某個模塊,有沒有權限啟動某個模塊,異步加載某些模塊,模塊之間的依賴關系,某些模塊啟動失敗了怎么辦。這些邊界條件都可以交給它來判斷,而其他模塊只關心實現(xiàn)自己邏輯就好了
最后打個比方,中介模塊真的就像房屋中介一樣!如果你是房東,你只需要下令一聲“我要找人租房”,他們就自然會生成那個列表,你不用直接和房客打交道。
1.JavaScript 的事件處理是所有瀏覽器端程序的基本必備技巧。當目標元素的事件被觸發(fā)時,比如按鈕被點擊,鼠標移動,或者是表單提交,這些事件觸發(fā)時都可以觸發(fā)對應的方法。當然這個過程中我們可以傳遞一些參數(shù)過去來自定義很多事情。
一個要注意避免的就是事件與DOM元素的緊耦合。比如先看看下面這個代碼,考慮到用一個簡單表單來接受用戶輸入的信息。
form id="msgbox" action="#" method="get"
label for="msg"your message/label
input id="msg" value="" /
buttonSEND/button
/form
2.我們能寫一段代碼讓屏幕上顯示剛才表單提交的信息
document.getElementById("msgbox").addEventListener("submit", function(e) {
e.preventDefault();
var msg = e.currentTarget.getElementById("msg").value.trim();
if (msg) {
alert(msg);
}
}, false);
那么如果我們想對顯示出來的這句話做一些操作,比如發(fā)一條tweet,或存儲在服務器或者干些其他什么?則有兩個選擇:
1,對已有的事件處理方法添加代碼
這個方案的缺陷就是每當打算測試或者更新后來添加的事件處理方法時變得非常不彈性化,每當更改或者刪除一些功能的時候,總會有一大段代碼要跟著去修改。
2,為每一個功能都創(chuàng)建事件處理方法
第二個方法很好的解決了前面方法的問題,雖然這個方法可能會一開始麻煩點。畢竟所有的方法代碼都要處理重復的消息提取以及驗證步驟。
設想假如能夠自行觸發(fā)自定義的"newMessage"事件而無需驗證是否有message提交,或假如能監(jiān)控整個HTML文檔或者body這樣的標簽而不僅僅只是某個表單的節(jié)點,能否做到呢?這就是自定義事件要解決的問題了。
自行觸發(fā)一個自定義事件是很簡單的;如下代碼就是傳遞一個name,details以及options到新建的 CustomEvent對象中:
var event = new CustomEvent(
"newMessage",
{
detail: {
message: "Hello World!",
time: new Date(),
},
bubbles: true,
cancelable: true
}
);
這個案例中,"newMessage"是一個自定義事件類型。而第二個參數(shù)包含了此對象的三個屬性(detail,bubbles,cancelable)。
detail: 包含了自定義事件的具體信息,這里僅僅就包括了一個message與一個time
bubbles: 如果是true,則事件會一直傳遞給自身的父對象元素,接著父對象也會觸發(fā)此類事件
cancelable: 如果是true, 事件可以被事件觸發(fā)元素的 stopPropagation( ) 方法停止
現(xiàn)在,我們需要針對某個特定元素來觸發(fā)此類事件。
document.getElementById("msgbox").dispatchEvent(event);
1. 通過MobX來解決
?? MobX 是一個簡單、高效的前端狀態(tài)管理腳本庫。 根據(jù)文檔,Just do something to the state and MobX will make sure your app respects the changes。
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function () {
this.firstName + ' ' + this.lastName;
}
});
注意下區(qū)別,mobx.observable是以下做的唯一區(qū)別,再來看下console.log輸出的內容。
mobx.autorun(function () {
console.log('first name: ' + person.firstName);
});
person.age = 38; // 不打印內容
person.lastName = 'RUBY!'; // 仍然不打印內容
person.firstName = 'Matthew!'; // 打印內容
通過使用autorun, MobX只會檢測使用到的內容。如果覺得不夠清楚,來看下下面的:
mobx.autorun(function () {
console.log('Full name: ' + person.fullName);
});
person.age = 38; // 不打印內容
person.lastName = 'RUBY!'; // 打印內容
person.firstName = 'Matthew!'; // 打印內容
相信你已經理解了。
MobX核心概念
observer
var log = function(data) {
$('#output').append('pre' +data+ '/pre');
}
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 34
});
log(person.firstName);
person.firstName = 'Mike';
log(person.firstName);
person.firstName = 'Lissy';
log(person.firstName);
MobX可觀察的對象都是普通的對象。這個例子中我們沒有觀察任何內容,這里例子也展示了怎樣將MobX使用到項目中。只需要使用mobx.observable()或 mobx.extendObservable()就可以了。
autorun
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0
});
mobx.autorun(function () {
log(person.firstName + ' ' + person.age);
});
// this will print Matt NN 10 times
_.times(10, function () {
person.age = _.random(40);
});
// this will print nothing
_.times(10, function () {
person.lastName = _.random(40);
});
當變量值改變時,肯定想做一些事情,所以使用autorun(),將會在任何一個觀察的內容改變時觸發(fā)回調。注意下上面的例子中age改變時autorun()不會觸發(fā)。
computed
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0,
get fullName () {
return this.firstName + ' ' + this.lastName;
}
});
log(person.fullName);
person.firstName = 'Mike';
log(person.fullName);
person.firstName = 'Lissy';
log(person.fullName);
2. 注意下fullName()方法沒有使用參數(shù)和get的用法,MobX會自動創(chuàng)建一個計算的值。。person.fullName有什么不同的地方?這是一個函數(shù),但是沒有調用就獲取到了結果!通常,使用的是person.fullName(),而不是person.fullName。這里就是遇到的第一個getter函數(shù)。
不僅如此,MobX還會監(jiān)聽計算值得依賴的變量,并且只在它們修改的時候運行觸發(fā)更新。如果沒有修改,將會直接返回之前緩存中的值。看下面一個例子。
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 0,
get fullName () {
// Note how this computed value is cached.
// We only hit this function 3 times.
log('-- hit fullName --');
return this.firstName + ' ' + this.lastName;
}
});
mobx.autorun(function () {
log(person.fullName + ' ' + person.age);
});
// this will print Matt Ruby NN 10 times
_.times(10, function () {
person.age = _.random(40);
});
person.firstName = 'Mike';
person.firstName = 'Lissy';
3. 這里使用了person.fullName很多次,但是函數(shù)只有在firstName和lastName修改時才會運行。這就是MobX可以提供應用速度的一種方式。
使用MobX進行項目實踐
直接看例子:
h1Test/h1
script
var person = {
events: {},
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function() {
return this.firstName + ' ' + this.lastName;
},
setPersonData: function(personData) {
$.extend(this, personData);
$(this.events).trigger('changed', personData);
}
};
var renderCount = 0;
$(person.events).on('changed', function() {
renderCount += 1;
$('h1').text(person.fullName() + ' render count: '+ renderCount);
});
// this will trigger every time
_.times(10, function() {
person.setPersonData({age: _.random(20)});
});
/script
4. 這里name被重復渲染了10次,盡管沒有修改firstName或lastName,可使用多個事件來優(yōu)化這里,在渲染之前做判斷。但是太復雜了。下面是MobX的例子;
h1Test/h1
script
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
get fullName () {
return this.firstName + ' ' + this.lastName;
}
});
var renderCount = 0;
mobx.autorun(function() {
renderCount++;
$('h1').text(person.fullName + ' render count: ' + renderCount);
});
// this will trigger the render one time
_.times(10, function() {
person.setPersonData({
age: _.random(20)
});
});
/script
注意下這里為什么沒有事件、觸發(fā)器或on綁定。使用MobX,你會使用修改后的最新值。而且它只被渲染一次,這是因為我們沒有修改內容時autorun只是一直在監(jiān)聽。
// observable person
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37
});
// reduce the person to simple html
var printObject = function(objectToPrint) {
return _.reduce(objectToPrint, function(result, value, key) {
result += key + ': ' + value + 'br/';
return result;
}, '');
};
// print out the person anytime there's a change
mobx.autorun(function(){
$('#person').html(printObject(person));
});
// watch all the input for changes and update the person
// object accordingly.
$('input').on('keyup', function(event) {
person[event.target.name] = $(this).val();
});
這里我們能編輯person的所有信息來觀察輸出的內容情況,現(xiàn)在這個例子中有個小小的問題,這里input的值和person的值不同步,這里改下:
mobx.autorun(function(){
$('#person').html(printObject(person));
// update the input values
_.forIn(person, function(value, key) {
$('input[name="'+key+'"]').val(value);
});
});
這里雖有重新渲染。看到的就是為什么很多人選擇使用React的原因。React允許你將輸出內容拆分小的組件進行個別的渲染。
MobX不一定在實際項目中使用,如果需要這種細粒度的操作使用React就可以了。當在實際項目中使用MobX和jQuery時,使用autorun()就可以解決很多問題了。
當前名稱:javascript訂閱,發(fā)布者訂閱者模式 js
分享網(wǎng)址:http://chinadenli.net/article11/dsshodd.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設計、自適應網(wǎng)站、定制開發(fā)、網(wǎng)頁設計公司、、網(wǎng)站建設
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)