成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

wendux / 707人閱讀

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

1 什么是裝飾器模式

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

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

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

2 ESnext中的裝飾器模式

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

目前Decorator的語法還只是一個(gè)提案,如果期望現(xiàn)在使用裝飾器模式,需要安裝配合babel + webpack并結(jié)合插件實(shí)現(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,可能還需要在項(xiàng)目根目錄下添加以下tsconfig.json文件來組織一個(gè)ts檢查的報(bào)錯(cuò)。

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

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

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

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

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

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

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

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

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

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實(shí)現(xiàn)了this的綁定,并在get中利用Object.defineProperty重寫了該方法,將value定義為通過bind綁定后的函數(shù)boundFn,以此實(shí)現(xiàn)了this永遠(yuǎn)指向?qū)嵗?。下面我們?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實(shí)現(xiàn)函數(shù)防抖

函數(shù)防抖(debounce)在前端項(xiàng)目中有著很多的應(yīng)用,例如在resizescroll等事件中操作DOM,或?qū)τ脩糨斎雽?shí)現(xiàn)實(shí)時(shí)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這個(gè)類,其中updateContent方法會在用戶輸入時(shí)執(zhí)行并可能有一些消耗性能的DOM操作,這里我們在該方法內(nèi)部打印了傳入的參數(shù)以驗(yàn)證調(diào)用過程。可以看到4次的調(diào)用結(jié)果分別為1 3 2 4。

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

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

  return function (target, key, descriptor) {

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

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

上面的方法中,我們采用了ES6提供的Map數(shù)據(jù)結(jié)構(gòu)去實(shí)現(xiàn)實(shí)例化對象和延時(shí)器的映射。在函數(shù)的內(nèi)部,首先清除延時(shí)器,接著設(shè)置延時(shí)執(zhí)行函數(shù),這是實(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 4。1由于在400ms內(nèi)被重復(fù)調(diào)用而沒有被打印,這符合我們的參數(shù)為500的預(yù)期。

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

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

下面我們來實(shí)現(xiàn)一個(gè)@deprecate的裝飾器,其實(shí)這類的裝飾器也可以擴(kuò)展成為打印日志裝飾器@log,上報(bào)信息裝飾器@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ù)接受一個(gè)對象參數(shù),該參數(shù)分別有infourl兩個(gè)鍵值,其中info填入警告信息,url為選填的詳情網(wǎng)頁地址。下面我們來為一個(gè)名為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)用了一個(gè)要在下個(gè)版本被移除的方法");
// DEPRECATION MyLib#deprecatedMethod: The methods will be deprecated in next version. See http://www.baidu.com for more detail
// 調(diào)用了一個(gè)要在下個(gè)版本被移除的方法
3 總結(jié)

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

參考文獻(xiàn)

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

淘寶前端團(tuán)隊(duì):ES7 Decorator 裝飾者模式

阮一峰:ECMAScript 6 入門

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

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

相關(guān)文章

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

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

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

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

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

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

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

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

    shleyZ 評論0 收藏0

發(fā)表評論

0條評論

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