国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

從ES6重新認(rèn)識JavaScript設(shè)計模式: 裝飾器模式

wendux / 679人閱讀

摘要:什么是裝飾器模式向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)的設(shè)計模式被稱為裝飾器模式,它是作為現(xiàn)有的類的一個包裝。中的裝飾器模式中有一個的提案,使用一個以開頭的函數(shù)對中的及其屬性方法進(jìn)行修飾。

1 什么是裝飾器模式

向一個現(xiàn)有的對象添加新的功能,同時又不改變其結(jié)構(gòu)的設(shè)計模式被稱為裝飾器模式(Decorator Pattern),它是作為現(xiàn)有的類的一個包裝(Wrapper)。

可以將裝飾器理解為游戲人物購買的裝備,例如LOL中的英雄剛開始游戲時只有基礎(chǔ)的攻擊力和法強。但是在購買的裝備后,在觸發(fā)攻擊和技能時,能夠享受到裝備帶來的輸出加成。我們可以理解為購買的裝備給英雄的攻擊和技能的相關(guān)方法進(jìn)行了裝飾。

這里推薦一篇淘寶前端團隊的博文,很有趣的以鋼鐵俠的例子來講解了裝飾者模式。

2 ESnext中的裝飾器模式

ESnext中有一個Decorator的提案,使用一個以 @ 開頭的函數(shù)對ES6中的class及其屬性、方法進(jìn)行修飾。Decorator的詳細(xì)語法請參考阮一峰的《ECMASciprt入門 —— Decorator》。

目前Decorator的語法還只是一個提案,如果期望現(xiàn)在使用裝飾器模式,需要安裝配合babel + webpack并結(jié)合插件實現(xiàn)。

npm安裝依賴

npm install babel-core babel-loader babel-plugin-transform-decorators babel-plugin-transform-decorators-legacy babel-preset-env

配置.babelrc文件

{
  "presets": ["env"],
  "plugins": ["transform-decorators-legacy"]
}

webpack.config.js中添加babel-loader

  module: {
    rules: [
      { test: /.js$/, exclude: /node_modules/, loader: "babel-loader" }
    ],
  }

如果你使用的IDE為Visual Studio Code,可能還需要在項目根目錄下添加以下tsconfig.json文件來組織一個ts檢查的報錯。

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "allowJs": true,
    "lib": [
      "es6"
    ],
  }
}

下面我將實現(xiàn)3個裝飾器,分別為@autobind@debounce@deprecate

2.1 @autobind實現(xiàn)this指向原對象

在JavaScript中,this的指向問題一直是一個老生常談的話題,在Vue或React這類框架的使用過程中,新手很有可能一不小心就丟失了this的指向?qū)е路椒ㄕ{(diào)用錯誤。例如下面一段代碼:

class Person {
  getPerson() {
    return this;
  }
}

let person = new Person();
let { getPerson } = person;

console.log(getPerson() === person); // false

上面的代碼中, getPerson方法中的this默認(rèn)指向Person類的實例,但是如果將Person通過解構(gòu)賦值的方式提取出來,那么此時的this指向為undefined。所以最終的打印結(jié)果為false

此時我們可以實現(xiàn)一個autobind的函數(shù),用來裝飾getPerson這個方法,實現(xiàn)this永遠(yuǎn)指向Person的實例。

function autobind(target, key, descriptor) {
  var fn = descriptor.value;
  var configurable = descriptor.configurable;
  var enumerable = descriptor.enumerable;

  // 返回descriptor
  return {
    configurable: configurable,
    enumerable: enumerable,
    get: function get() {
      // 將該方法綁定this
      var boundFn = fn.bind(this);
      // 使用Object.defineProperty重新定義該方法
      Object.defineProperty(this, key, {
        configurable: true,
        writable: true,
        enumerable: false,
        value: boundFn
      })

      return boundFn;
    }
  }
}

我們通過bind實現(xiàn)了this的綁定,并在get中利用Object.defineProperty重寫了該方法,將value定義為通過bind綁定后的函數(shù)boundFn,以此實現(xiàn)了this永遠(yuǎn)指向?qū)嵗O旅嫖覀優(yōu)?b>getPerson方法加上裝飾并調(diào)用。

class Person {
  @autobind
  getPerson() {
    return this;
  }
}

let person = new Person();
let { getPerson } = person;

console.log(getPerson() === person); // true
2.2 @debounce實現(xiàn)函數(shù)防抖

函數(shù)防抖(debounce)在前端項目中有著很多的應(yīng)用,例如在resizescroll等事件中操作DOM,或?qū)τ脩糨斎雽崿F(xiàn)實時ajax搜索等會被高頻的觸發(fā),前者會對瀏覽器性能產(chǎn)生直觀的影響,后者會對服務(wù)器產(chǎn)生較大的壓力,我們期望這類高頻連續(xù)觸發(fā)的事件在觸發(fā)結(jié)束后再做出響應(yīng),這就是函數(shù)防抖的應(yīng)用。

class Editor {
  constructor() {
    this.content = "";
  }

  updateContent(content) {
    console.log(content);
    this.content = content;
    // 后面有一些消耗性能的操作
  }
}

const editor1 = new Editor();
editor1.updateContent(1);
setTimeout(() => { editor1.updateContent(2); }, 400);


const editor2= new Editor();
editor2.updateContent(3);
setTimeout(() => { editor2.updateContent(4); }, 600);

// 打印結(jié)果: 1 3 2 4

上面的代碼中我們定義了Editor這個類,其中updateContent方法會在用戶輸入時執(zhí)行并可能有一些消耗性能的DOM操作,這里我們在該方法內(nèi)部打印了傳入的參數(shù)以驗證調(diào)用過程。可以看到4次的調(diào)用結(jié)果分別為1 3 2 4

下面我們實現(xiàn)一個debounce函數(shù),該方法傳入一個數(shù)字類型的timeout參數(shù)。

function debounce(timeout) {
  const instanceMap = new Map(); // 創(chuàng)建一個Map的數(shù)據(jù)結(jié)構(gòu),將實例化對象作為key

  return function (target, key, descriptor) {

    return Object.assign({}, descriptor, {
      value: function value() {

        // 清除延時器
        clearTimeout(instanceMap.get(this));
        // 設(shè)置延時器
        instanceMap.set(this, setTimeout(() => {
          // 調(diào)用該方法
          descriptor.value.apply(this, arguments);
          // 將延時器設(shè)置為 null
          instanceMap.set(this, null);
        }, timeout));
      }
    })
  }
}

上面的方法中,我們采用了ES6提供的Map數(shù)據(jù)結(jié)構(gòu)去實現(xiàn)實例化對象和延時器的映射。在函數(shù)的內(nèi)部,首先清除延時器,接著設(shè)置延時執(zhí)行函數(shù),這是實現(xiàn)debounce的通用方法,下面我們來測試一下debounce裝飾器。

class Editor {
  constructor() {
    this.content = "";
  }

  @debounce(500)  
  updateContent(content) {
    console.log(content);
    this.content = content;
  }
}

const editor1 = new Editor();
editor1.updateContent(1);
setTimeout(() => { editor1.updateContent(2); }, 400);


const editor2= new Editor();
editor2.updateContent(3);
setTimeout(() => { editor2.updateContent(4); }, 600);

//打印結(jié)果: 3 2 4

上面調(diào)用了4次updateContent方法,打印結(jié)果為3 2 41由于在400ms內(nèi)被重復(fù)調(diào)用而沒有被打印,這符合我們的參數(shù)為500的預(yù)期。

2.3 @deprecate實現(xiàn)警告提示

在使用第三方庫的過程中,我們會時不時的在控制臺遇見一些警告,這些警告用來提醒開發(fā)者所調(diào)用的方法會在下個版本中被棄用。這樣的一行打印信息也許我們的常規(guī)做法是在方法內(nèi)部添加一行代碼即可,這樣其實在源碼閱讀上并不友好,也不符合單一職責(zé)原則。如果在需要拋出警告的方法前面加一個@deprecate的裝飾器來實現(xiàn)警告,會友好得多。

下面我們來實現(xiàn)一個@deprecate的裝飾器,其實這類的裝飾器也可以擴展成為打印日志裝飾器@log,上報信息裝飾器@fetchInfo等。

function deprecate(deprecatedObj) {

  return function(target, key, descriptor) {
    const deprecatedInfo = deprecatedObj.info;
    const deprecatedUrl = deprecatedObj.url;
    // 警告信息
    const txt = `DEPRECATION ${target.constructor.name}#${key}: ${deprecatedInfo}. ${deprecatedUrl ? "See "+ deprecatedUrl + " for more detail" : ""}`;
    
    return Object.assign({}, descriptor, {
      value: function value() {
        // 打印警告信息
        console.warn(txt);
        descriptor.value.apply(this, arguments);
      }
    })
  }
}

上面的deprecate函數(shù)接受一個對象參數(shù),該參數(shù)分別有infourl兩個鍵值,其中info填入警告信息,url為選填的詳情網(wǎng)頁地址。下面我們來為一個名為MyLib的庫的deprecatedMethod方法添加該裝飾器吧!

class MyLib {
  @deprecate({
    info: "The methods will be deprecated in next version", 
    url: "http://www.baidu.com"
  })
  deprecatedMethod(txt) {
    console.log(txt)
  }
}

const lib = new MyLib();
lib.deprecatedMethod("調(diào)用了一個要在下個版本被移除的方法");
// DEPRECATION MyLib#deprecatedMethod: The methods will be deprecated in next version. See http://www.baidu.com for more detail
// 調(diào)用了一個要在下個版本被移除的方法
3 總結(jié)

通過ESnext中的裝飾器實現(xiàn)裝飾器模式,不僅有為類擴充功能的作用,而且在閱讀源碼的過程中起到了提示作用。上面所舉到的例子只是結(jié)合裝飾器的新語法和裝飾器模式做了一個簡單封裝,請勿用于生產(chǎn)環(huán)境。如果你現(xiàn)在已經(jīng)體會到了裝飾器模式的好處,并想在項目中大量使用,不妨看一下core-decorators這個庫,其中封裝了很多常用的裝飾器.

參考文獻(xiàn)

IMWeb的前端博客:淺談JS中的裝飾器模式

淘寶前端團隊:ES7 Decorator 裝飾者模式

阮一峰:ECMAScript 6 入門

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/96841.html

相關(guān)文章

  • 裝飾模式的應(yīng)用:react高階組件和ES6 裝飾

    摘要:裝飾者模式參與者裝飾者和被裝飾者共同的父類,是一個接口或者抽象類,用來定義基本行為定義具體對象,即被裝飾者抽象裝飾者,繼承自,從外類來擴展。三裝飾器高階組件可以看做是裝飾器模式在的實現(xiàn)。 一 裝飾者模式 優(yōu)先使用對象組合而不是類繼承。 --《設(shè)計模式》 1.什么是裝飾者模式 定義:動態(tài)的給對象添加一些額外的屬性或行為。相比于使用繼承,裝飾者模式更加靈活。 2.裝飾者模式參與者 Co...

    YuboonaZhang 評論0 收藏0
  • 設(shè)計模式(通往高手之路的必備技能)

    摘要:設(shè)計模式的定義在面向?qū)ο筌浖O(shè)計過程中針對特定問題的簡潔而優(yōu)雅的解決方案。從前由于使用的局限性,和做的應(yīng)用相對簡單,不被重視,就更談不上設(shè)計模式的問題。 ‘從大處著眼,從小處著手’,以前對這句話一知半解,自從踏出校門走入社會,開始工作以來,有了越來越深的理解,偶有發(fā)現(xiàn)這句話用在程序開發(fā)中也有用,所以,近段時間開始嘗試著分析jQuery源碼,分析angularjs源碼,學(xué)習(xí)設(shè)計模式。 設(shè)...

    paraller 評論0 收藏0
  • 每天一個設(shè)計模式裝飾模式

    摘要:作者按每天一個設(shè)計模式旨在初步領(lǐng)會設(shè)計模式的精髓,目前采用和兩種語言實現(xiàn)。誠然,每種設(shè)計模式都有多種實現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶崿F(xiàn)方式原文地址是每天一個設(shè)計模式之裝飾者模式歡迎關(guān)注個人技術(shù)博客。 作者按:《每天一個設(shè)計模式》旨在初步領(lǐng)會設(shè)計模式的精髓,目前采用javascript和python兩種語言實現(xiàn)。誠然,每種設(shè)計模式都有多種實現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶崿F(xiàn)方式...

    brianway 評論0 收藏0
  • 每天一個設(shè)計模式裝飾模式

    摘要:作者按每天一個設(shè)計模式旨在初步領(lǐng)會設(shè)計模式的精髓,目前采用和兩種語言實現(xiàn)。誠然,每種設(shè)計模式都有多種實現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶崿F(xiàn)方式原文地址是每天一個設(shè)計模式之裝飾者模式歡迎關(guān)注個人技術(shù)博客。 作者按:《每天一個設(shè)計模式》旨在初步領(lǐng)會設(shè)計模式的精髓,目前采用javascript和python兩種語言實現(xiàn)。誠然,每種設(shè)計模式都有多種實現(xiàn)方式,但此小冊只記錄最直截了當(dāng)?shù)膶崿F(xiàn)方式...

    shleyZ 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<