<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你應(yīng)該知道的 4 種 JavaScript 設(shè)計模式

          共 7184字,需瀏覽 15分鐘

           ·

          2022-02-27 02:53

          英文 | https://www.digitalocean.com/community/tutorial_series/javascript-design-patterns

          翻譯 | 楊小愛


          前言
          每個開發(fā)人員都努力編寫可維護、可讀和可重用的代碼。隨著應(yīng)用程序變得越來越大,代碼結(jié)構(gòu)變得越來越重要。設(shè)計模式被證明是解決這一挑戰(zhàn)的關(guān)鍵——為特定情況下的常見問題提供組織結(jié)構(gòu)。
          JavaScript Web 開發(fā)人員在創(chuàng)建應(yīng)用程序時經(jīng)常與設(shè)計模式交互,甚至是在不知不覺中。
          本文涵蓋最重要和最常用的4種 JavaScript 設(shè)計模式的解釋和示例。
          正文
          JavaScript 模塊是最常用的設(shè)計模式,用于保持特定代碼段獨立于其他組件。這提供了松散耦合以支持結(jié)構(gòu)良好的代碼。
          對于那些熟悉面向?qū)ο笳Z言的人來說,模塊就是 JavaScript “類”。類的眾多優(yōu)點之一是封裝——保護狀態(tài)和行為不被其他類訪問。模塊模式允許公共和私有(加上鮮為人知的受保護和特權(quán))訪問級別。
          模塊應(yīng)該是立即調(diào)用函數(shù)表達(dá)式 (IIFE) 以允許私有范圍 - 即保護變量和方法的閉包(但是,它將返回一個對象而不是函數(shù))。
          這是它的樣子:
          (function() {
          // declare private variables and/or functions
          return { // declare public variables and/or functions }
          })();

          在這里,我們在返回我們想要返回的對象之前實例化私有變量和/或函數(shù)。我們閉包之外的代碼無法訪問這些私有變量,因為它不在同一個范圍內(nèi)。讓我們看一個更具體的實現(xiàn):

          var HTMLChanger = (function() {    var contents = 'contents'
          var changeHTML = function() { var element = document.getElementById('attribute-to-change'); element.innerHTML = contents; }
          return { callChangeHTML: function() { changeHTML(); console.log(contents); } };
          })();
          HTMLChanger.callChangeHTML(); // Outputs: 'contents'console.log(HTMLChanger.contents); // undefined

          請注意, callChangeHTML 綁定到返回的對象,并且可以在 HTMLChanger 命名空間中引用。但是,在模塊之外時,無法引用內(nèi)容。

          揭示模塊模式

          模塊模式的一種變體稱為顯示模塊模式。目的是維護封裝并揭示對象字面量中返回的某些變量和方法。直接實現(xiàn)如下所示:

          var Exposer = (function() {    var privateVariable = 10;
          var privateMethod = function() { console.log('Inside a private method!'); privateVariable++; }
          var methodToExpose = function() { console.log('This is a method I want to expose!'); }
          var otherMethodIWantToExpose = function() { privateMethod(); }
          return { first: methodToExpose, second: otherMethodIWantToExpose };})();
          Exposer.first(); // Output: This is a method I want to expose!Exposer.second(); // Output: Inside a private method!Exposer.methodToExpose; // undefined

          盡管這看起來更簡潔,但一個明顯的缺點是無法引用私有方法。這可能會帶來單元測試挑戰(zhàn)。同樣,公共行為是不可覆蓋的。

          JavaScript 中的觀察者設(shè)計模式

          很多時候,當(dāng)應(yīng)用程序的一部分發(fā)生變化時,其他部分也需要更新。在 AngularJS 中,如果 $scope 對象更新,可以觸發(fā)一個事件來通知另一個組件。觀察者模式就是這樣結(jié)合的——如果一個對象被修改,它會向依賴對象廣播發(fā)生了變化。

          另一個主要的例子是模型-視圖-控制器(MVC)架構(gòu)。模型更改時視圖會更新。一個好處是將視圖與模型分離以減少依賴關(guān)系。

          維基百科上的觀察者設(shè)計模式

          如 UML 圖中所示,必要的對象是主體、觀察者和具體對象。主題包含對具體觀察者的引用,以通知任何更改。Observer 對象是一個抽象類,它允許具體的觀察者實現(xiàn) notify 方法。

          讓我們看一個通過事件管理包含觀察者模式的 AngularJS 示例。

          // Controller 1$scope.$on('nameChanged', function(event, args) {    $scope.name = args.name;});
          ...
          // Controller 2$scope.userNameChanged = function(name) { $scope.$emit('nameChanged', {name: name});};

          對于觀察者模式,區(qū)分獨立對象或主體很重要。

          值得注意的是,盡管觀察者模式確實提供了許多優(yōu)點,但缺點之一是隨著觀察者數(shù)量的增加,性能會顯著下降。最臭名昭著的觀察者之一是觀察者模式。在 AngularJS 中,我們可以觀察變量、函數(shù)和對象。$$digest 循環(huán)運行并在修改范圍對象時向每個觀察者通知新值。

          我們可以在 JavaScript 中創(chuàng)建自己的主題和觀察者。讓我們看看這是如何實現(xiàn)的:

          var Subject = function() {    this.observers = [];
          return { subscribeObserver: function(observer) { this.observers.push(observer); }, unsubscribeObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers.splice(index, 1); } }, notifyObserver: function(observer) { var index = this.observers.indexOf(observer); if(index > -1) { this.observers[index].notify(index); } }, notifyAllObservers: function() { for(var i = 0; i < this.observers.length; i++){ this.observers[i].notify(i); }; } };};
          var Observer = function() { return { notify: function(index) { console.log("Observer " + index + " is notified!"); } }}
          var subject = new Subject();
          var observer1 = new Observer();var observer2 = new Observer();var observer3 = new Observer();var observer4 = new Observer();
          subject.subscribeObserver(observer1);subject.subscribeObserver(observer2);subject.subscribeObserver(observer3);subject.subscribeObserver(observer4);
          subject.notifyObserver(observer2); // Observer 2 is notified!
          subject.notifyAllObservers();// Observer 1 is notified!// Observer 2 is notified!// Observer 3 is notified!// Observer 4 is notified!

          發(fā)布/訂閱

          然而,發(fā)布/訂閱模式使用位于希望接收通知的對象(訂閱者)和觸發(fā)事件的對象(發(fā)布者)之間的主題/事件通道。此事件系統(tǒng)允許代碼定義特定于應(yīng)用程序的事件,這些事件可以傳遞包含訂閱者所需值的自定義參數(shù)。這里的想法是避免訂閱者和發(fā)布者之間的依賴關(guān)系。

          這與觀察者模式不同,因為任何訂閱者都實現(xiàn)了適當(dāng)?shù)氖录幚沓绦騺碜院徒邮瞻l(fā)布者廣播的主題通知。

          盡管存在區(qū)別,但許多開發(fā)人員選擇將發(fā)布/訂閱設(shè)計模式與觀察者聚合。發(fā)布/訂閱模式中的訂閱者通過某種消息傳遞媒介得到通知,但觀察者通過實現(xiàn)類似于主題的處理程序得到通知。

          在 AngularJS 中,訂閱者使用 $on('event', callback) 來“訂閱”事件,而發(fā)布者使用 $emit('event', args) 或 $broadcast('event', args) 來“發(fā)布”事件.

          JavaScript 中的原型設(shè)計模式

          任何 JavaScript 開發(fā)人員要么看到過關(guān)鍵字原型,被原型繼承弄糊涂了,要么在他們的代碼中實現(xiàn)了原型。Prototype 設(shè)計模式依賴于 JavaScript 原型繼承。原型模型主要用于在性能密集型情況下創(chuàng)建對象。

          創(chuàng)建的對象是傳遞的原始對象的克?。\克?。?。原型模式的一個用例是執(zhí)行廣泛的數(shù)據(jù)庫操作以創(chuàng)建用于應(yīng)用程序其他部分的對象。如果另一個進程需要使用這個對象,而不是必須執(zhí)行這個大量的數(shù)據(jù)庫操作,克隆先前創(chuàng)建的對象將是有利的。

          維基百科上的原型設(shè)計模式

          這個 UML 描述了如何使用原型接口來克隆具體的實現(xiàn)。

          要克隆一個對象,必須存在一個構(gòu)造函數(shù)來實例化第一個對象。接下來,通過使用關(guān)鍵字原型變量和方法綁定到對象的結(jié)構(gòu)。

          讓我們看一個基本的例子:

          var TeslaModelS = function() {    this.numWheels    = 4;    this.manufacturer = 'Tesla';    this.make         = 'Model S';}
          TeslaModelS.prototype.go = function() { // Rotate wheels}
          TeslaModelS.prototype.stop = function() { // Apply brake pads}

          構(gòu)造函數(shù)允許創(chuàng)建單個 TeslaModelS 對象。當(dāng)創(chuàng)建新的 TeslaModelS 對象時,它會保留在構(gòu)造函數(shù)中初始化的狀態(tài)。此外,維護函數(shù) go 和 stop 很容易,因為我們用原型聲明了它們。一種在原型上擴展函數(shù)的同義方式,如下所述:

          var TeslaModelS = function() {    this.numWheels    = 4;    this.manufacturer = 'Tesla';    this.make         = 'Model S';}
          TeslaModelS.prototype = { go: function() { // Rotate wheels }, stop: function() { // Apply brake pads }}

          揭示原型模式

          與模塊模式類似,原型模式也有一個顯著的變化。顯示原型模式提供了對公共和私有成員的封裝,因為它返回一個對象字面量。

          由于我們要返回一個對象,我們將在原型對象前面加上一個函數(shù)。通過擴展上面的示例,我們可以選擇要在當(dāng)前原型中公開的內(nèi)容以保留其訪問級別:

          var TeslaModelS = function() {    this.numWheels    = 4;    this.manufacturer = 'Tesla';    this.make         = 'Model S';}
          TeslaModelS.prototype = function() {
          var go = function() { // Rotate wheels };
          var stop = function() { // Apply brake pads };
          return { pressBrakePedal: stop, pressGasPedal: go }
          }();

          請注意,由于超出了返回對象的范圍,函數(shù) stop 和 go 將如何與返回對象屏蔽。由于 JavaScript 原生支持原型繼承,因此無需重寫底層特性。

          JavaScript 中的單例設(shè)計模式

          Singleton 只允許單個實例化,但同一對象的多個實例。Singleton 限制客戶端創(chuàng)建多個對象,在創(chuàng)建第一個對象后,它將返回自身的實例。

          對于大多數(shù)以前沒有使用過單例的人來說,很難找到單例的用例。一個例子是使用辦公室打印機。如果一個辦公室有十個人,他們都使用一臺打印機,十臺計算機共享一臺打印機(實例)。通過共享一臺打印機,他們共享相同的資源。

          var printer = (function () {
          var printerInstance;
          function create () {
          function print() { // underlying printer mechanics }
          function turnOn() { // warm up // check for paper }
          return { // public + private states and behaviors print: print, turnOn: turnOn }; }
          return { getInstance: function() { if(!printerInstance) { printerInstance = create(); } return printerInstance; } };
          function Singleton () { if(!printerInstance) { printerInstance = intialize(); } };
          })();

          create 方法是私有的,因為我們不希望客戶端訪問它,但是請注意 getInstance 方法是公共的。每個職員可以通過與 getInstance 方法交互來生成一個打印機實例,如下所示:

          var officePrinter = printer.getInstance();

          在 AngularJS 中,Singleton 很普遍,最值得注意的是服務(wù)、工廠和提供者。由于它們維護狀態(tài)并提供資源訪問,因此創(chuàng)建兩個實例會破壞共享服務(wù)/工廠/提供者的觀點。

          當(dāng)多個線程嘗試訪問同一資源時,多線程應(yīng)用程序中會出現(xiàn)競爭條件。單例容易受到競爭條件的影響,例如,如果沒有先初始化實例,則兩個線程可以創(chuàng)建兩個對象,而不是返回和實例。這違背了單例的目的。因此,開發(fā)人員在多線程應(yīng)用程序中實現(xiàn)單例時必須了解同步。

          結(jié)論

          設(shè)計模式經(jīng)常用于大型應(yīng)用程序中,但要了解,在某些時候某些地方,其中一種模式可能優(yōu)于另一種的模式,需要實踐。

          在構(gòu)建任何應(yīng)用程序之前,您應(yīng)該徹底考慮每個參與者以及他們?nèi)绾蜗嗷ソ换?。在回顧了模塊、原型、觀察者和單例設(shè)計模式之后,您應(yīng)該能夠識別這些模式并在項目中使用它們。



          學(xué)習(xí)更多技能

          請點擊下方公眾號

          瀏覽 28
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲 日韩 欧美 一区 | 亚洲欧美动漫 | 日日操天天 | 狠狠躁日日躁夜夜躁2022麻豆 | 国产操逼精品免费录像视频 |