亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

使用AmplifyJS組件配合JavaScript進行編程的指南

2019-11-20 11:58:32
字體:
來源:轉載
供稿:網友

事件分發的作用

在為頁面添加各類交互功能時,我們熟知的最簡單的做法就是為頁面元素綁定事件,然后在事件處理函數中,做我們想要做的動作。就像這樣的代碼:

element.onclick = function(event){  // Do anything.};

如果我們要做的動作不復雜,那么實際邏輯功能的代碼,放在這里是可以的。如果今后需要修改,再到這段事件處理函數的位置來修改。

再進一步,為了做適當的代碼復用,我們可能會把邏輯功能中的一部分分拆到一個函數內:

element.onclick = function(event){  // Other code here.  doSomethingElse();};

這里的函數doSomethingElse對應的功能可能會在其他地方用到,所以會這樣做分拆。此外,可能會有設定坐標這樣的功能(假定函數名為setPosition),則還需要用到瀏覽器事件對象event提供的諸如指針位置一類的信息:

element.onclick = function(event){  // Other code here.  doSomethingElse();  setPosition(event.clientX, event.clientY);};

此處有一個不推薦的做法是直接把event對象傳遞給setPosition。這是因為,分清邏輯功能和事件偵聽兩種職責,是一種良好的實踐。只讓事件處理函數本身接觸到瀏覽器事件對象event,有利于降低代碼耦合,方便獨立測試及維護。

那么,功能越來越多,越來越復雜了會怎么樣呢?如果沿用之前的做法,可能是這個樣子:

element.onclick = function(event){  doMission1();  doMission2(event.clientX, event.clientY);  doMission3();  // ...  doMissionXX();};

雖然這樣用也沒問題,但這種時候其實就可以考慮更優雅的寫法:

element.onclick = function(event){  amplify.publish( "aya:clicked", {    x: event.clientX,    y: event.clientY  });};

這種形式就是事件分發,請注意,這里的事件并不是指瀏覽器原生的事件(event對象),而是邏輯層面的自定義事件。上面的aya:clicked就是一個隨便寫(really?)的自定義事件名稱。

顯然到這還沒結束,為了完成之前的復雜的功能,我們還需要將自定義事件和要做的事關聯在一起:

amplify.subscribe( "aya:clicked", doMission1);// ...amplify.subscribe( "aya:clicked", doMission2);// ...

看起來又繞了回來?沒錯,但這是有用的。一方面,瀏覽器原生事件的偵聽被分離并固化了下來,以后如果邏輯功能有變化,例如減少幾個功能,則只需要到自定義事件的關聯代碼部分做刪減,而不需要再關心原生事件。另一方面,邏輯功能的調整變得更為靈活,可以在任意的代碼位置通過subscribe添加功能,而且可以自行做分類管理(自定義的事件名)。

簡單來說,事件分發通過增加一層自定義事件的冗余(在只有簡單的邏輯功能時,你就會覺得它是冗余),降低了代碼模塊之間的耦合度,使得邏輯功能更為清晰有條理,便于后續維護。

等下,前面那個出境了好幾次的很有存在感的amplify是干什么的?

Nice,終于是時候介紹這個了。
AmplifyJS

事件分發是需要一定的方法來實現的。實現事件分發的設計模式之一,就是發布/訂閱(Publish/Subscribe)。

AmplifyJS是一個簡單的JavaScript庫,主要提供了Ajax請求、數據存儲、發布/訂閱三項功能(每一項都可獨立使用)。其中,發布/訂閱是核心功能,對應命名是amplify.core。

2015728151503102.jpg (342×85)

amplify.core是發布/訂閱設計模式的一個簡潔的、清晰的實現,加上注釋一共100多行。讀完amplify的源碼,就可以比較好地理解如何去實現一個發布/訂閱的設計模式。
代碼全貌

amplify.core的源碼整體結構如下:

(function( global, undefined ) {var slice = [].slice,  subscriptions = {};var amplify = global.amplify = {  publish: function( topic ) {    // ...  },  subscribe: function( topic, context, callback, priority ) {    // ...  },  unsubscribe: function( topic, context, callback ) {    // ...  }};}( this ) );

可以看到,amplify定義了一個名為amplify的全局變量(作為global的屬性),它有3個方法publish、subscribe、unsubscribe。此外,subscriptions作為一個局部變量,它將保存發布/訂閱模式涉及的所有自定義事件名及其關聯函數。
publish

publish即發布,它要求指定一個topic,也就是自定義事件名(或者就叫做話題),調用后,所有關聯到某個topic的函數,都將被依次調用:

publish: function( topic ) {  // [1]  if ( typeof topic !== "string" ) {    throw new Error( "You must provide a valid topic to publish." );  }  // [2]  var args = slice.call( arguments, 1 ),    topicSubscriptions,    subscription,    length,    i = 0,    ret;  if ( !subscriptions[ topic ] ) {    return true;  }  // [3]  topicSubscriptions = subscriptions[ topic ].slice();  for ( length = topicSubscriptions.length; i < length; i++ ) {    subscription = topicSubscriptions[ i ];    ret = subscription.callback.apply( subscription.context, args );    if ( ret === false ) {      break;    }  }  return ret !== false;},

[1],參數topic必須要求是字符串,否則拋出一個錯誤。

[2],args將取得除topic之外的其他所有傳遞給publish函數的參數,并以數組形式保存。如果對應topic在subscriptions中沒有找到,則直接返回。

[3],topicSubscriptions作為一個數組,取得某一個topic下的所有關聯元素,其中每一個元素都包括callback及context兩部分。然后,遍歷元素,調用每一個關聯元素的callback,同時帶入元素的context和前面的額外參數args。如果任意一個關聯元素的回調函數返回false,則停止運行其他的并返回false。
subscribe

訂閱,如這個詞自己的含義那樣(就像訂本雜志什么的),是建立topic和callback的關聯的步驟。比較特別的是,amplify在這里還加入了priority(優先級)的概念,優先級的值越小,優先級越高,默認是10。優先級高的callback,將會在publish的時候,被先調用。這個順序的原理可以從前面的publish的源碼中看到,其實就是預先按照優先級從高到低依次排列好了某一topic的所有關聯元素。

subscribe: function( topic, context, callback, priority ) {    if ( typeof topic !== "string" ) {      throw new Error( "You must provide a valid topic to create a subscription." );    }    // [1]    if ( arguments.length === 3 && typeof callback === "number" ) {      priority = callback;      callback = context;      context = null;    }    if ( arguments.length === 2 ) {      callback = context;      context = null;    }    priority = priority || 10;    // [2]    var topicIndex = 0,      topics = topic.split( //s/ ),      topicLength = topics.length,      added;    for ( ; topicIndex < topicLength; topicIndex++ ) {      topic = topics[ topicIndex ];      added = false;      if ( !subscriptions[ topic ] ) {        subscriptions[ topic ] = [];      }      // [3]      var i = subscriptions[ topic ].length - 1,        subscriptionInfo = {          callback: callback,          context: context,          priority: priority        };      // [4]      for ( ; i >= 0; i-- ) {        if ( subscriptions[ topic ][ i ].priority <= priority ) {          subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo );          added = true;          break;        }      }      // [5]      if ( !added ) {        subscriptions[ topic ].unshift( subscriptionInfo );      }    }    return callback;  },

[1],要理解這一部分,請看amplify提供的API示意:

amplify.subscribe( string topic, function callback )amplify.subscribe( string topic, object context, function callback )amplify.subscribe( string topic, function callback, number priority )amplify.subscribe(  string topic, object context, function callback, number priority )

可以看到,amplify允許多種參數形式,而當參數數目和類型不同的時候,位于特定位置的參數可能會被當做不同的內容。這也在其他很多JavaScript庫中可以見到。像這樣,通過參數數目和類型的判斷,就可以做到這種多參數形式的設計。

[2],訂閱的時候,topic是允許空格的,空白符將被當做分隔符,認為是將一個callback關聯到多個topic上,所以會使用一個循環。added用作標識符,表明新加入的這個元素是否已經添加到數組內,初始為false。

[3],每一個callback的保存,實際是一個對象,除callback外還帶上了context(默認為null)和priority。

[4],這個循環是在根據priority的值,找到關聯元素應處的位置。任何topic的關聯元素都是從無到有,且依照priority數值從小到大排列(已排序的)。因此,在比較的時候,是先假設新加入的元素的priority數值較大(優先級低),從數組尾端向前比較,只要原數組中有關聯元素的priority數值比新加入元素的小,循環就可以中斷,且可以確定地用數組的splice方法將新加入的元素添加在此。如果循環一直運行到完畢,則可以確定新加入的元素的priority數值是最小的,此時added將保持為初始值false。

[5],如果到這個位置,元素還沒有被添加,那么執行添加,切可以確定元素應該位于數組的最前面(或者是第一個元素)。
unsubscribe

雖然發布和訂閱是最主要的,但也會有需要退訂的時候(雜志不想看了果斷退?。?。所以,還會需要一個unsubscribe。

unsubscribe: function( topic, context, callback ) {  if ( typeof topic !== "string" ) {    throw new Error( "You must provide a valid topic to remove a subscription." );  }  if ( arguments.length === 2 ) {    callback = context;    context = null;  }  if ( !subscriptions[ topic ] ) {    return;  }  var length = subscriptions[ topic ].length,    i = 0;  for ( ; i < length; i++ ) {    if ( subscriptions[ topic ][ i ].callback === callback ) {      if ( !context || subscriptions[ topic ][ i ].context === context ) {        subscriptions[ topic ].splice( i, 1 );                // Adjust counter and length for removed item        i--;        length--;      }    }  }}

讀過前面的源碼后,這部分看起來就很容易理解了。根據指定的topic遍歷關聯元素,找到callback一致的,然后刪除它。由于使用的是splice方法,會直接修改原始數組,因此需要手工對i和length再做一次調整。
Amplify使用示例

官方提供的其中一個使用示例是:

amplify.subscribe( "dataexample", function( data ) {  alert( data.foo ); // bar});//...amplify.publish( "dataexample", { foo: "bar" } );

結合前面的源碼部分,是否對發布/訂閱這一設計模式有了更明確的體會呢?
補充說明

你可能也注意到了,AmplifyJS所實現的典型的發布/訂閱是同步的(synchronous)。也就是說,在運行amplify.publish(topic)的時候,是會沒有任何延遲地把某一個topic附帶的所有回調,全部都運行一遍。
結語

Pub/Sub是一個比較容易理解的設計模式,但非常有用,可以應對大型應用的復雜邏輯。本文簡析的AmplifyJS是我覺得寫得比較有章法而且簡明切題(針對單一功能)的JavaScript庫,所以在此分享給大家。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩免费在线免费观看| 欧美精品videossex88| 久久国产精品影视| 久久久久久久久久婷婷| 国产精品最新在线观看| 色综合91久久精品中文字幕| 欧美日韩亚洲91| 欧美福利小视频| 欧洲美女免费图片一区| 亚洲激情中文字幕| 精品国产一区二区三区久久狼5月| 欧美性极品少妇精品网站| 亚洲无线码在线一区观看| 国产人妖伪娘一区91| www.亚洲一二| 亚洲色在线视频| 国产精品91免费在线| 亚洲精品久久久久久久久久久久久| 91免费看片网站| 九色精品美女在线| 91精品在线看| 国产99久久久欧美黑人| 亚洲欧美在线播放| 国产午夜精品理论片a级探花| 精品小视频在线| 日韩国产一区三区| 日韩激情在线视频| 久久理论片午夜琪琪电影网| 日韩最新中文字幕电影免费看| 国产精品久久久久久久久借妻| 日韩av网址在线| 国产成人短视频| 中文字幕不卡av| 国产精品国产福利国产秒拍| 亚洲aa中文字幕| 欧美老女人在线视频| 热久久这里只有| 日韩av手机在线观看| 欧美激情视频在线观看| 欧美午夜久久久| 欧美专区在线观看| 最近的2019中文字幕免费一页| 91久久精品一区| 欧美极品少妇xxxxⅹ裸体艺术| 欧洲精品久久久| 国产精品揄拍500视频| 日本道色综合久久影院| 国产精品久久久久久久av电影| 尤物yw午夜国产精品视频明星| 国产精品日韩在线观看| 欧美性xxxxxx| 欧美激情视频在线| 富二代精品短视频| 久久精品一本久久99精品| 在线成人一区二区| 亚洲免费影视第一页| 国外成人在线直播| 中文字幕欧美日韩va免费视频| 亚洲免费影视第一页| 欧美电影在线观看网站| 日韩精品视频在线观看免费| 欧美日韩成人黄色| 欧美成人小视频| 欧美日韩综合视频网址| 欧美性感美女h网站在线观看免费| 91久久在线播放| 久久免费少妇高潮久久精品99| 国产精品第1页| 国产亚洲精品美女| 亚洲黄色av女优在线观看| 国产成人+综合亚洲+天堂| 欧美另类在线播放| 亚洲最大的成人网| 日韩免费观看视频| 91成人国产在线观看| 亚洲最大成人在线| 日韩av在线免费播放| 久热精品视频在线观看| 日本精品一区二区三区在线播放视频| 欧美日韩精品二区| 亚洲人成网站777色婷婷| 日韩免费观看高清| 久久久精品国产网站| 丝袜情趣国产精品| 日韩在线视频国产| 国产精品久久久久福利| 一夜七次郎国产精品亚洲| 国产欧美韩国高清| 91国产精品视频在线| 欧美日韩久久久久| 岛国av一区二区三区| 久久乐国产精品| 国产亚洲激情在线| 精品偷拍各种wc美女嘘嘘| 国外色69视频在线观看| 亚洲精品久久久久久久久久久久| 综合国产在线视频| 国产欧美日韩综合精品| 成人亚洲激情网| 亚洲精品xxxx| 777午夜精品福利在线观看| 日韩av大片在线| 久久久精品日本| 麻豆国产va免费精品高清在线| 欧美激情国产高清| 国产玖玖精品视频| 日韩激情视频在线播放| 国产精品美女www爽爽爽视频| 中文字幕亚洲欧美一区二区三区| 亚洲精品福利视频| 国产精品成人观看视频国产奇米| 国产综合香蕉五月婷在线| 精品国产欧美一区二区五十路| 欧美日韩一二三四五区| 日韩电影中文字幕在线观看| 亚洲免费精彩视频| 久久人91精品久久久久久不卡| 久久偷看各类女兵18女厕嘘嘘| 92福利视频午夜1000合集在线观看| 91手机视频在线观看| 精品偷拍一区二区三区在线看| 欧美亚洲在线视频| 国产亚洲欧洲高清| 久久久久一本一区二区青青蜜月| 日韩av在线一区| 亚洲毛茸茸少妇高潮呻吟| 久久99青青精品免费观看| 中文字幕欧美精品在线| 国产在线一区二区三区| 国产成人精品一区二区在线| 88xx成人精品| 国产91av在线| 亚洲在线观看视频| 国产最新精品视频| 欧美午夜片欧美片在线观看| 国产亚洲精品一区二555| 成人www视频在线观看| 午夜精品久久久久久久久久久久久| 国产精品扒开腿做爽爽爽视频| 亚洲精品国产品国语在线| 欧美日韩激情视频| 欧美色另类天堂2015| 日韩精品福利网站| 色一情一乱一区二区| 91精品久久久久久久久久久久久久| 精品视频久久久久久久| 一区二区三区美女xx视频| 国产91精品久| 日韩中文字幕视频在线| 亚洲国产精品久久久久久| 久久久人成影片一区二区三区观看| 精品视频偷偷看在线观看| 欧美综合激情网| 亚洲国产欧美久久| 国产精品成人免费电影| 欧美大片第1页| 成人av资源在线播放| 91成人国产在线观看| 国产激情999| 欧美激情视频给我| 国产一区二区三区三区在线观看| 色视频www在线播放国产成人| 色妞在线综合亚洲欧美| 在线视频国产日韩|