亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解JavaScript中的客戶端消息框架設(shè)計(jì)原理

 更新時(shí)間:2015年06月24日 14:47:03   投稿:goldensun  
這篇文章主要介紹了詳解JavaScript中的客戶端消息框架設(shè)計(jì)原理,包括客戶端和服務(wù)器端的通信等方面的內(nèi)容,需要的朋友可以參考下

 哇——是個(gè)危險(xiǎn)的題目,對(duì)嗎?我們對(duì)于什么是本質(zhì)的理解當(dāng)然會(huì)隨著我們對(duì)要解決問題的理解而變化。因此我不會(huì)說謊——一年前我所理解的本質(zhì)很不幸并不完整,因?yàn)槲掖_信我將要寫的已經(jīng)快伴隨我有6個(gè)月之久。所以,這篇文章是我在發(fā)現(xiàn)JavaScript中成功的運(yùn)用客戶端消息模式的一些關(guān)鍵要點(diǎn)時(shí)的一個(gè)掠影。

1.) 理解中介者與觀察者的區(qū)別 
 大多數(shù)人在描述任何事件/消息機(jī)制的時(shí)候喜歡套用“發(fā)布者/訂閱者”(pub/sub)——但我認(rèn)為這個(gè)術(shù)語不能很好的與抽象建立聯(lián)系。當(dāng)然,從根本上說,一些東西訂閱了另一些東西發(fā)布的事件。但是發(fā)布者與訂閱者在何等層次上封裝在一起有可能使一個(gè)好的模式變得暗淡無光。那么,區(qū)別在什么地方呢?


觀察者

觀察者模式包括了被一個(gè)或多個(gè)觀察者所觀察的某個(gè)對(duì)象。典型的,該對(duì)象記錄下所有觀察者的痕跡,通常是用一個(gè)list來存儲(chǔ)觀察者注冊(cè)的回調(diào)方法,這些是觀察者為了接收通知而訂閱的。 注意: (哦,雙關(guān)語,我有多愛他們啊)(譯者注:Observe 觀察、注意)
 

var observer = {
 listen : function() {
  console.log("Yay for more cliché examples...");
 }
};
var elem = document.getElementById("cliche");
elem.addEventListener("click", observer.listen);

一些需要注意的事情是:

  •     我們必須獲得對(duì)此對(duì)象的直接引用
  •     此對(duì)象必須保持一些內(nèi)部的狀態(tài),保存觀察者的回調(diào)痕跡
  •     有時(shí)偵聽者不會(huì)利用由此對(duì)象返回的任何參數(shù),理論上來說,有可能有 0-n*個(gè)參數(shù) (更多是取決于以后會(huì)變得多有趣)

* n事實(shí)上不是無限的,但為了討論的目的,它指我們永遠(yuǎn)也達(dá)不到的極限


中介者

中介者模式在一個(gè)對(duì)象與一個(gè)觀察者之間引入了一個(gè)“第三方”——有效的將二者解耦而且將他們之間如何通信封裝起來。一個(gè)中介者的API可能像“發(fā)布”、“訂閱”、“取消訂閱”一樣簡單,或者某個(gè)領(lǐng)域范圍內(nèi)的實(shí)現(xiàn)可能被提供用來隱藏這些方法于某些更有意義的語義之中。大多數(shù)我用過的服務(wù)器端的實(shí)現(xiàn)更傾向于領(lǐng)域范圍而不是更簡單,但是并沒有對(duì)一個(gè)通用的中介者有任何規(guī)則限制!并不罕見,有種想法認(rèn)為一個(gè)通用的中介者是一種信息經(jīng)紀(jì)人。無論何種情形,結(jié)果都一樣——特定對(duì)象與觀察者之間不再互相直接知曉:
 

// It's fun to be naive!
var mediator = {
 _subs: {},
 // a real subscribe would at least check to make sure the
 // same callback instance wasn't registered 2x.
 // Sheesh, where did they find this guy?!
 subscribe: function(topic, callback) {
  this._subs[topic] = this._subs[topic] || [];
  this._subs[topic].push(callback);
 },
 // lolwut? No ability to pass function context? :-)
 publish : function(topic, data) {
  var subs = this._subs[topic] || [];
  subs.forEach(function(cb) {
   cb(data);
  });
 }
}
var FatherTime = function(med) { this.mediator = med; };
FatherTime.prototype.wakeyWakey = function() {
 this.mediator.publish("alarm.clock", {
  time: "06:00 AM",
  canSnooze: "heck-no-get-up-lazy-bum"
 });
}
var Developer = function(mediator) {
 this.mediator = mediator;
 this.mediator.subscribe("alarm.clock", this.pleaseGodNo);
};
Developer.prototype.pleaseGodNo = function(data) {
 alert("ZOMG, it's " + data.time + ". Please just make it stop.");
}
var fatherTime = new FatherTime(mediator);
var developer = new Developer(mediator);
fatherTime.wakeyWakey();

你可能會(huì)想,除了特別純粹的中介者實(shí)現(xiàn),特定對(duì)象不再負(fù)有保存訂閱者列表的責(zé)任,而且“時(shí)光老人”(FatherTime)與“開發(fā)者”(Developer)實(shí)例永遠(yuǎn)沒法真正互相知道。他們只是共享了一個(gè)信息——將如我們今后所見,這是一個(gè)很重要的合約。 “很好,Jim。這對(duì)我而言仍然是發(fā)布者/訂閱者,那么重點(diǎn)呢?我選擇某個(gè)方向真的會(huì)有區(qū)別嗎?”哦,繼續(xù)吧,親愛的讀者們,繼續(xù)吧。

2.) 了解什么時(shí)候使用中介者和觀察者

使用本地的觀察者和中介者,即寫在組件當(dāng)中的,而中介者看起來又像遠(yuǎn)程的組件間通信。不管怎樣。我對(duì)待這種情況的原則雖然是——tl;dr(too long; don't read)(太長,不讀了)。但無論如何,反正串聯(lián)在一起最好。

要我簡捷地說真是麻煩,就像把幾個(gè)月來的細(xì)致體驗(yàn)壓縮到裝不下140個(gè)字的溝里。現(xiàn)實(shí)中回答這個(gè)問題肯定不簡潔。所以有一個(gè)長版本的解釋:

    觀察者除了關(guān)心數(shù)據(jù)映射之外還有必要引用別的項(xiàng)目嗎?例如Backbone.View視圖有各種理由直接引用它的模型。這是非常自然的關(guān)系,視圖不僅要在模型改變時(shí)進(jìn)行渲染,還需要調(diào)用模型的事件處理。如果段首的問題答案是”yes“,那觀察者就是有意義的。
    如果觀察者和觀察對(duì)象的關(guān)系僅僅是依賴數(shù)據(jù),那我愿意使用中介pub/sub方式。兩個(gè)Backbone.View視圖或模型之間的通信,用觀察者是合適的。比如控制導(dǎo)航菜單的視圖發(fā)出的信息,是面包屑(breadcrumb)掛件需要的(響應(yīng)當(dāng)前的層級(jí))。掛件不需要引用導(dǎo)航視圖,它只需要導(dǎo)航視圖提供信息。更關(guān)鍵的,導(dǎo)航視圖也許不是唯一的信息來源,別的視圖可能也可以提供。此時(shí),中介pub/sub模式是最理想的——而且自身擴(kuò)展性良好。

看起來這樣又好又全面,但是其實(shí)還有一個(gè)露點(diǎn):如果我給對(duì)象定義一個(gè)本地事件,既想要觀察者直接調(diào)用,又可以被訂閱者間接訪問到,怎么辦?這就是我為什么說要串聯(lián)在一起:你推送或者橋接本地事件到消息組去吧。需要些更多代碼?很有可能——但是總比你把觀察對(duì)象傳遞給所有觀察者,一直緊耦合下去的情況好。然后,我們可以很好地繼續(xù)以下兩點(diǎn)...


3.) 選擇性的“提交”本地事件到總線

最開始我?guī)缀踔挥糜^察者模式來在JavaScript中觸發(fā)事件。這是我們一次又一次遇到的模式,但更流行的客戶端輔助庫行為方式根本上來說是混合中介者的,給我們提供了就像它們是觀察者模式的API。我最初寫postal.js的時(shí)候,開始走進(jìn)“為所有事物搭中介”的階段。在我寫的原型與構(gòu)造函數(shù)中,分布各處的發(fā)布與訂閱的調(diào)用并不罕見。當(dāng)我從這個(gè)改變中自然的解耦受益時(shí),非基礎(chǔ)的代碼開始似乎充滿了相關(guān)于基礎(chǔ)的部分。構(gòu)造函數(shù)到處都要帶上一個(gè)通道,訂閱被當(dāng)作新實(shí)例的一部分被創(chuàng)建,原型方法直接發(fā)布一個(gè)數(shù)值到總線(甚至本地的訂閱者都不能直接的而必須監(jiān)聽總線以獲得信息)。將這些明顯關(guān)于總線的東西納入app的這些部分,開始像是代碼的味道。代碼的“敘述”似乎總是被打斷,如“噢,將這個(gè)向所有訂閱者發(fā)布出去”,“等等!等等!監(jiān)聽這個(gè)通道那個(gè)事情。好,現(xiàn)在繼續(xù)吧”。我的測(cè)試忽然開始需要依賴總線來做低層次的單元測(cè)試。而這感覺有點(diǎn)不對(duì)勁。

鐘擺擺動(dòng)的指向了中間,我認(rèn)識(shí)到我應(yīng)該保持一個(gè)“本地API”,并且在需要的時(shí)候通過一個(gè)中介者為應(yīng)用擴(kuò)展其可以觸及的數(shù)據(jù)。 例如,我的backbone視圖與模型,仍然用普通的Backbome.Events行為來給本地觀察者發(fā)送事件(就是說,模型的事件被它相應(yīng)的視圖所觀察)。當(dāng)app的其它部分需要知道模型的變化時(shí),我開始通過這些行將本地事件與總線橋接起來:
 

var SomeModel = Backbone.Model.extend({
 initialize: function() {
  this.on("change:superImportantField", function(model, value) {
   postal.publish({
    channel : "someChannel",
    topic : "omg.super.important.field.changed",
    data : {
    muyImportante: value,
    otherFoo: "otherBar"
    }
   });
  });
 }
});

重要的是要認(rèn)識(shí)到,當(dāng)有可能透明的推送事件到消息總線時(shí),本地事件和消息必須被認(rèn)為是分開的合約——至少概念上如此。換句話說,你要能夠修改“內(nèi)部的/本地的”事件而不破壞消息合約。這是要在腦海中記住的重要事實(shí)——否則你就是為緊耦合提供了一個(gè)新的途徑,在一個(gè)方法上走反了!

所以理所當(dāng)然,上述的模型是可以在沒有消息總線的情況下被測(cè)試。而且如果我移去橋接在本地事件與總線之間的邏輯,我的視圖與模型依然工作得毫無不暢。但是,這可是七行的例子(盡管格式化了)。 僅僅橋接四個(gè)事件就需要幾乎三十行的代碼。

噢,你怎樣才能二者兼顧呢—— 在適合直接觀察者時(shí)本地通知,同時(shí)使涉及事件可以擴(kuò)展,以便你的對(duì)象不必給所有對(duì)象都發(fā)送一圈——不需要代碼膨脹。通知怎樣才能很少的代碼又有更多的味道呢?

4.)在你的構(gòu)架中隱藏樣板

這并不是說上面的例子中的代碼 —— 將事件接入總線 —— 的語法或概念是錯(cuò)誤的(假設(shè)你接受本地和遠(yuǎn)程/橋接事件的概念)。然而,這是一個(gè)很好的體現(xiàn)在代碼基礎(chǔ)之上培養(yǎng)良好習(xí)慣的作用的例子。有時(shí)我們會(huì)聽到類似“代碼實(shí)在太多了”的抱怨(特別是當(dāng) LOC 作為代碼質(zhì)量的唯一判定者時(shí))。 當(dāng)這種情況下,我表示贊同。 它是一個(gè)可怕的樣板。  下面是我在橋接 Backbone 對(duì)象的本地事件到 postal.js 時(shí)使用的模式:
 

// the logic to wire up publications and subscriptions
// exists in our custom MsgBackboneView constructor
var SomeView = MsgBackboneView.extend({
 
 className : "i-am-classy",
 
 // bridging local events triggered by this view
 publications: {
 // This is the more common 'shorthand' syntax
 // The key name is the name of the event. The
 // value is "channel topic" in postal. So this
 // means the bridgeTooFar event will get
 // published to postal on the "comm" channel
 // using a topic of "thats.far.enough". By default
 // the 1st argument passed to the event callback
 // will become the message payload.
 bridgeTooFar : "comm thats.far.enough",
 
 // However, the longhand approach works like this:
 // The key is still the event name that will be bridged.
 // The value is an object that provides a channel name,
 // a topic (which can be a string or a function returning
 // a string), and an optional data function that returns
 // the object that should be the message payload.
 bridgeBurned: {
  channel : "comm",
  topic : "match.lit",
  data : function() {
   return { id: this.get("id"), foo: 'bar' };
  }
 },
 
 // This is how we subscribe to the bus and invoke
 // local methods to handle incoming messages
 subscriptions: {
  // The key is the name of the method to invoke.
  // The value is the "channel topic" to subscribe to.
  // So this will subscribe to the "hotChannel" channel
  // with a topic binding of "start.burning.*", and any
  // message arriving gets routed to the "burnItWithFire"
  // method on the view.
  burnItWithFire : "hotChannel start.burning.*"
 },
 
 burnItWithFire: function(data, envelope) {
  // do stuff with message data and/or envelope
 }
 
 // other wire-up, etc.
});


顯然你可以用幾種不同的方式做這些——選擇總線式的框架——這要比樣板方式少很多無關(guān)內(nèi)容,而且為Backbone開發(fā)人員所熟知。當(dāng)你同時(shí)控制事件發(fā)送器和消息總線的實(shí)現(xiàn)時(shí),橋接要更容易。這里有個(gè)將monologue.js發(fā)送器橋接到postal.js的例子: 
 

// using the 'monopost' add-on for monologue/postal:
// assuming we have a worker instance that has monologue
// methods on its prototype chain, etc. The keys are event
// topic bindings to match local events to, and if a match is
// found, it gets published to the channel specified in the
// value (using the same topic value)
worker.goPostal({
 "match.stuff.like.#" : "ThisChannelYo",
 "secret.sauce.*" : "SeeecretChannel",
 "another.*.topic" : "YayMoarChannelsChannel"
});

以不同的方式使用樣板是令人愉快的好習(xí)慣?,F(xiàn)在我可以分別獨(dú)立的測(cè)試我的本地對(duì)象,橋接代碼,甚至測(cè)試二者合一的生產(chǎn)&消費(fèi)期待的消息過程等等。

同樣重要的是要注意到,如果我需要在上述的場(chǎng)景訪問普通的postal API,沒有什么可以阻止我這么做。沒有丟失靈活性這么就等于成功了


5.) 消息是合約——要明智的選擇實(shí)現(xiàn)方式

有兩種將數(shù)據(jù)傳遞給訂閱者的方法——也許可以給他們貼上更“官方”的標(biāo)簽,我將如此描述他們:

  •     “0-n 參數(shù)”
  •     “封套” (或“單對(duì)象載荷“)

看看這些例子:
 

// 0-n args
this.trigger("someGuyBlogged", "Jim", "Cowart", "JavaScript");
// envelope style
this.emit("someGuyBlogged", {
 firstName: "Jim",
 lastName: "Cowart",
 category: "JavaScript"
});
/*
 In an emitter like monologue.js, the emit call above
 would actually publish an envelope that looked similar
 to this:
 {
  topic: "someGuyBlogged",
  timeStamp: "2013-02-05T04:54:59.209Z",
  data : {
   firstName: "Jim",
   lastName: "Cowart",
   category: "JavaScript"
  }
 }
*/

經(jīng)過一段時(shí)間,我發(fā)現(xiàn)封套方式比0-n參數(shù)方式要少很多很多麻煩(與代碼)。"0-n參數(shù)"途徑的挑戰(zhàn)主要在于兩個(gè)原因(就我的經(jīng)驗(yàn)而言):第一,很典型的是“當(dāng)事件觸發(fā)時(shí),你還記得要傳遞哪一個(gè)參數(shù)嗎?不記得?好,我想我會(huì)看看觸發(fā)的源頭”。不是一個(gè)真正意義上的好方法,對(duì)嗎?但它可以打斷代碼的正常流程。你可以用一個(gè)調(diào)試工具,檢測(cè)執(zhí)行條件下的參數(shù)值并由此推斷基于這些數(shù)值的”標(biāo)簽“,但哪個(gè)更簡單呢——看到一個(gè)”1.21“的參數(shù)值,困惑于它的意義,或者檢測(cè)一個(gè)對(duì)象并發(fā)現(xiàn){千兆瓦:1.21}。第二個(gè)原因是由于伴隨事件傳送可選的數(shù)據(jù),以及當(dāng)方法簽名變得更長帶來的痛苦。


"說實(shí)話,Jim,你這是在搭車棚。"或許是的,但是一段時(shí)間以來我一直看到代碼的基礎(chǔ)在擴(kuò)充與變形,簡單的包含一兩個(gè)參數(shù)的原始事件,在其間包含了可選的參數(shù)以后開始變得畸形:
 

// 最開始是這樣的
this.trigger("someEvent", "a string!", 99);
// 有一天, 它變得包含了一切
this.trigger("someEvent", "string", 99, { sky: "blue" }, [1,2,3,4], true, 0);
// 可是等等——第4和第5個(gè)參數(shù)是可選的,因此也可能傳的是:
this.trigger("someEvent", "string", 99, [1,2,3,4], true, 0);
// 噢,你還檢查第5個(gè)參數(shù)的真/假嗎?
// 哎呦!現(xiàn)在是早先的參數(shù)了……
this.trigger("someEvent", "string", 99, true, 0);

如果有任何數(shù)據(jù)是可選的,將沒有圍繞它的測(cè)試。但需要更少的代碼,需要能更具擴(kuò)展性,特別典型的是能自解釋(感謝這些成員名字)以便能在逐一傳送給訂閱者回調(diào)方法時(shí),對(duì)一個(gè)對(duì)象進(jìn)行那種測(cè)試。我仍然在不得不用"0-n參數(shù)"的地方用它,但如果由我決定,將是一直用封套的方法——我的事件發(fā)送者和消息總線都是這樣。(說明我存在偏見,monologue與postal共享同一個(gè)封套的數(shù)據(jù)結(jié)構(gòu),去掉了monologue不用的通道)

因此——得承認(rèn)用來給訂閱者傳輸數(shù)據(jù)的結(jié)構(gòu)是”合約“的一個(gè)部分。在封套方式這個(gè)方向,你可以用額外的元數(shù)據(jù)描述事件(不需要增加額外的參數(shù))——這保持了方法簽名(這就是合約的一個(gè)部分)對(duì)每個(gè)事件和訂閱者一致。你也能很容易的為一個(gè)信息結(jié)構(gòu)編制版本(或在必要的時(shí)候增加其他封套層級(jí)的信息)。如果你沿著這個(gè)方向做的話,請(qǐng)確保用的是一致的封套結(jié)構(gòu)。


6.) 消息”拓?fù)洹氨饶阆氲倪€重要

這里沒有銀彈。但是你要對(duì)如何命名主題與通道,以及如何設(shè)計(jì)消息載荷的結(jié)構(gòu)深思熟慮。我傾向于用兩種方法之一映射我的模型:用一個(gè)單一的數(shù)據(jù)通道,主題的前綴采用模型的名字,后跟其唯一的id,然后通過它的操作({modelType.id.operation})處理,或者給模型的自身通道,主題就是{id.operation}。一個(gè)恒定的習(xí)慣是在模型請(qǐng)求數(shù)據(jù)的時(shí)候自動(dòng)響應(yīng)這個(gè)行為。但并不是所有總線上的操作都是請(qǐng)求??赡苡泻唵蔚氖录l(fā)布到app。你是否命名主題來描述事件(理想條件下)?或者你是否掉進(jìn)了這樣的陷阱,通過命名主題來描述某個(gè)訂閱者可能的傾向行為?例如,包含“route.changed” 抑或 “show.customer.ui”主題的消息。一個(gè)表明了事件,另一個(gè)表明了命令。做這些決定的時(shí)候要仔細(xì)思考。命令并不壞,但在你需要請(qǐng)求/響應(yīng)或命令之前,你會(huì)為事件所能描述的數(shù)量而吃驚的。

相關(guān)文章

  • JavaScript arguments 多參傳值函數(shù)

    JavaScript arguments 多參傳值函數(shù)

    在一個(gè)函數(shù)體內(nèi),標(biāo)識(shí)符arguments引用了arguments對(duì)象的一個(gè)特殊屬性。可以按照數(shù)目(而不是名字)獲取傳遞給函數(shù)的參數(shù)值。
    2010-10-10
  • 關(guān)于JavaScript與HTML的交互事件

    關(guān)于JavaScript與HTML的交互事件

    本篇文章小編將為大家介紹,關(guān)于JavaScript與HTML的交互事件,有需要的朋友可以參考一下
    2013-04-04
  • JavaScript基本入門語法集合

    JavaScript基本入門語法集合

    對(duì)于新手來說,掌握下面的方法,基本上就可以自己些js了,入門必備
    2008-09-09
  • 詳解用JS添加和刪除class類名

    詳解用JS添加和刪除class類名

    這篇文章主要介紹了用JS添加和刪除class類名,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 如何用JavaScript定義一個(gè)類

    如何用JavaScript定義一個(gè)類

    其實(shí)Javascript中沒有類這個(gè)定義,但是有類這個(gè)概念。很多人都寫過這樣的代碼,就是一個(gè)關(guān)鍵字 function,然后定義一個(gè)方法名,方法名后緊跟一對(duì)括號(hào)。如果你在項(xiàng)目中寫過這樣的代碼,那么祝賀你,你可以不費(fèi)任何吹毛之力,就能一口氣讀完這篇文章了。
    2014-09-09
  • TypeScript開發(fā)環(huán)境安裝

    TypeScript開發(fā)環(huán)境安裝

    這篇文章介紹了TypeScript開發(fā)環(huán)境的安裝方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 使用JavaScript制作一個(gè)簡單的計(jì)數(shù)器的方法

    使用JavaScript制作一個(gè)簡單的計(jì)數(shù)器的方法

    這篇文章主要介紹了使用JavaScript制作一個(gè)簡單的計(jì)數(shù)器的方法,用于計(jì)算網(wǎng)頁用戶的來訪次數(shù),需要的朋友可以參考下
    2015-07-07
  • js中的caller和callee屬性介紹和例子

    js中的caller和callee屬性介紹和例子

    這篇文章主要介紹了js中的caller和callee屬性介紹和例子,需要的朋友可以參考下
    2014-06-06
  • 淺談JavaScript Array對(duì)象

    淺談JavaScript Array對(duì)象

    這篇文章主要介紹了JavaScript Array對(duì)象的相關(guān)資料,需要的朋友可以參考下
    2014-12-12

最新評(píng)論