在线看毛片视频-国产免费av在线-欧美日韩一区二区三区-国产成人无码av在线播放无广告-亚洲人va欧美va人人爽-国产第一草草-西班牙黄色片-四虎在线网站8848-最新av片免费网站入口-东京热无码中文字幕av专区-日本大人吃奶视频xxxx-欧美精品一区二区三区四区五区-国产片天天弄-国产免费内射又粗又爽密桃视频-欧美爱爱网站-日韩v欧美

當前位置:雨林木風下載站 > 網頁設計教程 > 詳細頁面

淺談ES6中的裝飾器

淺談ES6中的裝飾器

更新時間:2025-12-09 文章作者:未知 信息來源:網絡 閱讀次數:

網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創(chuàng)造出功能強大的網頁。因而,超級文本標記語言是萬維網(Web)編程的基礎,也就是說萬維網是建立...
網頁的本質就是超級文本標記語言,通過結合使用其他的Web技術(如:腳本語言、公共網關接口、組件等),可以創(chuàng)造出功能強大的網頁。因而,超級文本標記語言是萬維網(Web)編程的基礎,也就是說萬維網是建立在超文本基礎之上的。超級文本標記語言之所以稱為超文本標記語言,是因為文本中包含了所謂“超級鏈接”點。

本篇文章給大家?guī)淼膬热菔顷P于淺談ES6中的裝飾器,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

Decorator

裝飾器主要用于:

  1. 裝飾類

  2. 裝飾方法或屬性

裝飾類

@annotation
class MyClass { }

function annotation(target) {
   target.annotated = true;
}

裝飾方法或屬性

class MyClass {
  @readonly
  method() { }
}

function readonly(target, name, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

Babel

安裝編譯

我們可以在 Babel 官網的 Try it out,查看 Babel 編譯后的代碼。

不過我們也可以選擇本地編譯:

npm init

npm install --save-dev @babel/core @babel/cli

npm install --save-dev @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties

新建 .babelrc 文件

{
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-proposal-class-properties", {"loose": true}]
  ]
}

再編譯指定的文件

babel decorator.js --out-file decorator-compiled.js

裝飾類的編譯

編譯前:

@annotation
class MyClass { }

function annotation(target) {
   target.annotated = true;
}

編譯后:

var _class;

let MyClass = annotation(_class = class MyClass {}) || _class;

function annotation(target) {
  target.annotated = true;
}

我們可以看到對于類的裝飾,其原理就是:

@decorator
class A {}

// 等同于

class A {}
A = decorator(A) || A;

裝飾方法的編譯

編譯前:

class MyClass {
  @unenumerable
  @readonly
  method() { }
}

function readonly(target, name, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

function unenumerable(target, name, descriptor) {
  descriptor.enumerable = false;
  return descriptor;
}

編譯后:

var _class;

function _applyDecoratedDescriptor(target, property, decorators, descriptor, context ) {
    /**
     * 第一部分
     * 拷貝屬性
     */
    var desc = {};
    Object["ke" + "ys"](descriptor).forEach(function(key) {
        desc[key] = descriptor[key];
    });
    desc.enumerable = !!desc.enumerable;
    desc.configurable = !!desc.configurable;

    if ("value" in desc || desc.initializer) {
        desc.writable = true;
    }

    /**
     * 第二部分
     * 應用多個 decorators
     */
    desc = decorators
        .slice()
        .reverse()
        .reduce(function(desc, decorator) {
            return decorator(target, property, desc) || desc;
        }, desc);

    /**
     * 第三部分
     * 設置要 decorators 的屬性
     */
    if (context && desc.initializer !== void 0) {
        desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
        desc.initializer = undefined;
    }

    if (desc.initializer === void 0) {
        Object["define" + "Property"](target, property, desc);
        desc = null;
    }

    return desc;
}

let MyClass = ((_class = class MyClass {
    method() {}
}),
_applyDecoratedDescriptor(
    _class.prototype,
    "method",
    [readonly],
    Object.getOwnPropertyDescriptor(_class.prototype, "method"),
    _class.prototype
),
_class);

function readonly(target, name, descriptor) {
    descriptor.writable = false;
    return descriptor;
}

裝飾方法的編譯源碼解析

我們可以看到 Babel 構建了一個 _applyDecoratedDescriptor 函數,用于給方法裝飾。

Object.getOwnPropertyDescriptor()

在傳入參數的時候,我們使用了一個 Object.getOwnPropertyDescriptor() 方法,我們來看下這個方法:

Object.getOwnPropertyDescriptor() 方法返回指定對象上的一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)

順便注意這是一個 ES5 的方法。

舉個例子:

const foo = { value: 1 };
const bar = Object.getOwnPropertyDescriptor(foo, "value");
// bar {
//   value: 1,
//   writable: true
//   enumerable: true,
//   configurable: true,
// }

const foo = { get value() { return 1; } };
const bar = Object.getOwnPropertyDescriptor(foo, "value");
// bar {
//   get: /*the getter function*/,
//   set: undefined
//   enumerable: true,
//   configurable: true,
// }

第一部分源碼解析

在 _applyDecoratedDescriptor 函數內部,我們首先將 Object.getOwnPropertyDescriptor() 返回的屬性描述符對象做了一份拷貝:

// 拷貝一份 descriptor
var desc = {};
Object["ke" + "ys"](descriptor).forEach(function(key) {
    desc[key] = descriptor[key];
});
desc.enumerable = !!desc.enumerable;
desc.configurable = !!desc.configurable;

// 如果沒有 value 屬性或者沒有 initializer 屬性,表明是 getter 和 setter
if ("value" in desc || desc.initializer) {
    desc.writable = true;
}

那么 initializer 屬性是什么呢?Object.getOwnPropertyDescriptor() 返回的對象并不具有這個屬性呀,確實,這是 Babel 的 Class 為了與 decorator 配合而產生的一個屬性,比如說對于下面這種代碼:

class MyClass {
  @readonly
  born = Date.now();
}

function readonly(target, name, descriptor) {
  descriptor.writable = false;
  return descriptor;
}

var foo = new MyClass();
console.log(foo.born);

Babel 就會編譯為:

// ...
(_descriptor = _applyDecoratedDescriptor(_class.prototype, "born", [readonly], {
    configurable: true,
    enumerable: true,
    writable: true,
    initializer: function() {
        return Date.now();
    }
}))
// ...

此時傳入 _applyDecoratedDescriptor 函數的 descriptor 就具有 initializer 屬性。

第二部分源碼解析

接下是應用多個 decorators:

/**
 * 第二部分
 * @type {[type]}
 */
desc = decorators
    .slice()
    .reverse()
    .reduce(function(desc, decorator) {
        return decorator(target, property, desc) || desc;
    }, desc);

對于一個方法應用了多個 decorator,比如:

class MyClass {
  @unenumerable
  @readonly
  method() { }
}

Babel 會編譯為:

_applyDecoratedDescriptor(
    _class.prototype,
    "method",
    [unenumerable, readonly],
    Object.getOwnPropertyDescriptor(_class.prototype, "method"),
    _class.prototype
)

在第二部分的源碼中,執(zhí)行了 reverse() 和 reduce() 操作,由此我們也可以發(fā)現,如果同一個方法有多個裝飾器,會由內向外執(zhí)行。

第三部分源碼解析

/**
 * 第三部分
 * 設置要 decorators 的屬性
 */
if (context && desc.initializer !== void 0) {
    desc.value = desc.initializer ? desc.initializer.call(context) : void 0;
    desc.initializer = undefined;
}

if (desc.initializer === void 0) {
    Object["define" + "Property"](target, property, desc);
    desc = null;
}

return desc;

如果 desc 有 initializer 屬性,意味著當裝飾的是類的屬性時,會將 value 的值設置為:

desc.initializer.call(context)

而 context 的值為 _class.prototype,之所以要 call(context),這也很好理解,因為有可能

class MyClass {
  @readonly
  value = this.getNum() + 1;

  getNum() {
    return 1;
  }
}

最后無論是裝飾方法還是屬性,都會執(zhí)行:

Object["define" + "Property"](target, property, desc);

由此可見,裝飾方法本質上還是使用 Object.defineProperty() 來實現的。

應用

1.log

為一個方法添加 log 函數,檢查輸入的參數:

class Math {
  @log
  add(a, b) {
    return a + b;
  }
}

function log(target, name, descriptor) {
  var oldValue = descriptor.value;

  descriptor.value = function(...args) {
    console.log(`Calling ${name} with`, args);
    return oldValue.apply(this, args);
  };

  return descriptor;
}

const math = new Math();

// Calling add with [2, 4]
math.add(2, 4);

再完善點:

let log = (type) => {
  return (target, name, descriptor) => {
    const method = descriptor.value;
    descriptor.value =  (...args) => {
      console.info(`(${type}) 正在執(zhí)行: ${name}(${args}) = ?`);
      let ret;
      try {
        ret = method.apply(target, args);
        console.info(`(${type}) 成功 : ${name}(${args}) => ${ret}`);
      } catch (error) {
        console.error(`(${type}) 失敗: ${name}(${args}) => ${error}`);
      }
      return ret;
    }
  }
};

2.autobind

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

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

getPerson() === person;
// true

我們很容易想到的一個場景是 React 綁定事件的時候:

class Toggle extends React.Component {

  @autobind
  handleClick() {
      console.log(this)
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        button
      </button>
    );
  }
}

我們來寫這樣一個 autobind 函數:

const { defineProperty, getPrototypeOf} = Object;

function bind(fn, context) {
  if (fn.bind) {
    return fn.bind(context);
  } else {
    return function __autobind__() {
      return fn.apply(context, arguments);
    };
  }
}

function createDefaultSetter(key) {
  return function set(newValue) {
    Object.defineProperty(this, key, {
      configurable: true,
      writable: true,
      enumerable: true,
      value: newValue
    });

    return newValue;
  };
}

function autobind(target, key, { value: fn, configurable, enumerable }) {
  if (typeof fn !== 'function') {
    throw new SyntaxError(`@autobind can only be used on functions, not: ${fn}`);
  }

  const { constructor } = target;

  return {
    configurable,
    enumerable,

    get() {

      /**
       * 使用這種方式相當于替換了這個函數,所以當比如
       * Class.prototype.hasOwnProperty(key) 的時候,為了正確返回
       * 所以這里做了 this 的判斷
       */
      if (this === target) {
        return fn;
      }

      const boundFn = bind(fn, this);

      defineProperty(this, key, {
        configurable: true,
        writable: true,
        enumerable: false,
        value: boundFn
      });

      return boundFn;
    },
    set: createDefaultSetter(key)
  };
}

3.debounce

有的時候,我們需要對執(zhí)行的方法進行防抖處理:

class Toggle extends React.Component {

  @debounce(500, true)
  handleClick() {
    console.log('toggle')
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        button
      </button>
    );
  }
}

我們來實現一下:

function _debounce(func, wait, immediate) {

    var timeout;

    return function () {
        var context = this;
        var args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(function(){
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

function debounce(wait, immediate) {
  return function handleDescriptor(target, key, descriptor) {
    const callback = descriptor.value;

    if (typeof callback !== 'function') {
      throw new SyntaxError('Only functions can be debounced');
    }

    var fn = _debounce(callback, wait, immediate)

    return {
      ...descriptor,
      value() {
        fn()
      }
    };
  }
}

4.time

用于統(tǒng)計方法執(zhí)行的時間:

function time(prefix) {
  let count = 0;
  return function handleDescriptor(target, key, descriptor) {

    const fn = descriptor.value;

    if (prefix == null) {
      prefix = `${target.constructor.name}.${key}`;
    }

    if (typeof fn !== 'function') {
      throw new SyntaxError(`@time can only be used on functions, not: ${fn}`);
    }

    return {
      ...descriptor,
      value() {
        const label = `${prefix}-${count}`;
        count++;
        console.time(label);

        try {
          return fn.apply(this, arguments);
        } finally {
          console.timeEnd(label);
        }
      }
    }
  }
}

5.mixin

用于將對象的方法混入 Class 中:

const SingerMixin = {
  sing(sound) {
    alert(sound);
  }
};

const FlyMixin = {
  // All types of property descriptors are supported
  get speed() {},
  fly() {},
  land() {}
};

@mixin(SingerMixin, FlyMixin)
class Bird {
  singMatingCall() {
    this.sing('tweet tweet');
  }
}

var bird = new Bird();
bird.singMatingCall();
// alerts "tweet tweet"

mixin 的一個簡單實現如下:

function mixin(...mixins) {
  return target => {
    if (!mixins.length) {
      throw new SyntaxError(`@mixin() class ${target.name} requires at least one mixin as an argument`);
    }

    for (let i = 0, l = mixins.length; i < l; i++) {
      const descs = Object.getOwnPropertyDescriptors(mixins[i]);
      const keys = Object.getOwnPropertyNames(descs);

      for (let j = 0, k = keys.length; j < k; j++) {
        const key = keys[j];

        if (!target.prototype.hasOwnProperty(key)) {
          Object.defineProperty(target.prototype, key, descs[key]);
        }
      }
    }
  };
}

6.redux

實際開發(fā)中,React 與 Redux 庫結合使用時,常常需要寫成下面這樣。

class MyReactComponent extends React.Component {}

export default connect(mapStateToProps, mapDispatchToProps)(MyReactComponent);

有了裝飾器,就可以改寫上面的代碼。

@connect(mapStateToProps, mapDispatchToProps)
export default class MyReactComponent extends React.Component {};

相對來說,后一種寫法看上去更容易理解。

7.注意

以上我們都是用于修飾類方法,我們獲取值的方式為:

const method = descriptor.value;

但是如果我們修飾的是類的實例屬性,因為 Babel 的緣故,通過 value 屬性并不能獲取值,我們可以寫成:

const value = descriptor.initializer && descriptor.initializer();

以上就是淺談ES6中的裝飾器的詳細內容,更多請關注php中文網其它相關文章!


網站建設是一個廣義的術語,涵蓋了許多不同的技能和學科中所使用的生產和維護的網站。

溫馨提示:喜歡本站的話,請收藏一下本站!

本類教程下載

系統(tǒng)下載排行

在线看毛片视频-国产免费av在线-欧美日韩一区二区三区-国产成人无码av在线播放无广告-亚洲人va欧美va人人爽-国产第一草草-西班牙黄色片-四虎在线网站8848-最新av片免费网站入口-东京热无码中文字幕av专区-日本大人吃奶视频xxxx-欧美精品一区二区三区四区五区-国产片天天弄-国产免费内射又粗又爽密桃视频-欧美爱爱网站-日韩v欧美
  • <li id="86scu"><menu id="86scu"></menu></li>
    <li id="86scu"></li>
    <button id="86scu"></button>
  • <s id="86scu"></s><button id="86scu"><menu id="86scu"></menu></button>
  • 91精品无人成人www| 影音先锋男人的网站| 欧美在线一区视频| 亚洲天堂第一区| 特级西西人体www高清大胆| 做a视频在线观看| 日韩高清在线一区二区| 国产一区二区在线观看免费视频| 91极品视频在线观看| 亚洲激情在线观看视频| 性生活免费在线观看| 色www免费视频| 亚洲精品综合在线观看| 亚洲国产精品女人| 成人免费性视频| 每日在线更新av| 污污的网站18| 中国一级黄色录像| 波多野结衣之无限发射| 国产精品无码av无码| 午夜剧场在线免费观看| 日本三日本三级少妇三级66| 青青青在线观看视频| 国产一区亚洲二区三区| 色天使在线观看| h无码动漫在线观看| 国产情侣av自拍| 国产日韩欧美大片| 日本www在线播放| www.久久久久久久久久久| www.av91| av中文字幕网址| www国产精品内射老熟女| 亚洲不卡视频在线| 成人在线免费观看视频网站| 韩国日本美国免费毛片| 国产成人生活片| 91网址在线播放| 欧美人成在线观看| 欧美激情国内自拍| 午夜肉伦伦影院| 精品一二三四五区| 91网址在线观看精品| 毛片av免费在线观看| 欧美大黑帍在线播放| 午夜免费看毛片| 免费日韩视频在线观看| www.国产在线播放| 免费在线观看污污视频| 国产精品一区二区羞羞答答| 久久国产精品视频在线观看| 国产又爽又黄ai换脸| 日本一本二本在线观看| 特色特色大片在线| 美女在线视频一区二区| 欧美精品第三页| 日韩国产欧美亚洲| 中文精品无码中文字幕无码专区| 日本人69视频| 欧美第一页浮力影院| 农村妇女精品一二区| 国产免费观看高清视频| 国产又粗又猛又爽又黄的网站| 一级网站在线观看| 国产欧美一区二| 天天干天天曰天天操| 亚洲精品永久视频| 色噜噜狠狠一区二区三区狼国成人| 三级在线免费看| 久热精品在线观看视频| 中文字幕成人免费视频| gogogo高清免费观看在线视频| 日本久久久久久久久久久久| 五月婷婷激情久久| 亚洲午夜激情影院| 色乱码一区二区三区熟女| 香蕉视频xxxx| 日韩黄色片在线| 丰满爆乳一区二区三区| 黄色aaa级片| 手机在线国产视频| 强开小嫩苞一区二区三区网站 | 日本www.色| 999在线观看| 成人在线观看毛片| 成人午夜免费在线视频| 波多野结衣家庭教师在线| 国产综合免费视频| 亚洲欧美日韩三级| 17c丨国产丨精品视频| 亚洲爆乳无码专区| 色播五月激情五月| 99久热在线精品视频| 国产性xxxx18免费观看视频| 校园春色 亚洲色图| 国产一区一区三区| 国产最新免费视频| 不用播放器的免费av| 加勒比成人在线| 天天综合网日韩| 成年丰满熟妇午夜免费视频 | 大地资源第二页在线观看高清版| 日本天堂免费a| 亚洲36d大奶网| 99在线精品免费视频| 不卡中文字幕在线观看| 成年人网站免费视频| 黄色aaaaaa| 欧美一级黄色影院| 欧美亚洲日本一区二区三区| 波多野结衣国产精品| 免费日韩视频在线观看| 欧美无砖专区免费| www.午夜色| 在线看免费毛片| 中文字幕在线观看第三页| 国产一线二线三线女| 一区二区三区四区久久| 日韩一区二区三区不卡视频| www在线观看免费| 99久久久精品视频| 国产系列第一页| 国内av一区二区| 在线观看国产一级片| 亚洲熟女乱色一区二区三区| 97中文字幕在线| 亚洲精品国产suv一区88| 久久久精品视频国产| 午夜免费看视频| 五月天激情视频在线观看| 日韩亚洲在线视频| 97在线免费公开视频| 国产主播在线看| 成人黄色片视频| 日韩肉感妇bbwbbwbbw| 亚洲无吗一区二区三区| 日本激情视频在线| 污片在线免费看| 尤物国产在线观看| 视频免费1区二区三区| 97超碰人人看| 91.com在线| 免费在线观看亚洲视频| 国产免费毛卡片| 手机看片一级片| 视频一区二区视频| 亚洲 欧美 综合 另类 中字| 亚洲熟妇无码一区二区三区| 日韩av在线第一页| www.99在线| 99久热在线精品视频| 鲁一鲁一鲁一鲁一澡| 五月天婷婷激情视频| 中文字幕55页| 91视频 -- 69xx| 中文字幕视频三区| 91.com在线| 精品亚洲一区二区三区四区| 日本一道在线观看| 激情婷婷综合网| 香蕉视频免费版| 国产日韩一区二区在线观看| 欧美成人三级在线播放| 香港三级日本三级a视频| 免费av网址在线| 日韩精品福利片午夜免费观看| 国产精品333| 国产树林野战在线播放| 欧美日韩激情视频在线观看| 久久精品亚洲天堂| 欧美在线观看成人| 只有这里有精品| 亚洲一二三区av| 男人和女人啪啪网站| 免费观看黄色的网站| 国产理论在线播放| 免费av观看网址| 经典三级在线视频| 男女视频在线看| 国产福利一区视频| 成人黄色av片| 一本色道久久88亚洲精品综合| 五月婷婷之综合激情| 国产av天堂无码一区二区三区| 午夜激情视频网| 亚洲a级黄色片| 青青青国产在线视频| 91精品91久久久中77777老牛| 久久天天东北熟女毛茸茸| 天天干天天干天天干天天干天天干| 国产日本在线播放| av在线com| 成年人视频大全| 国产成人生活片| 国产激情片在线观看| 中文字幕免费高| 色一情一乱一乱一区91| 特级毛片在线免费观看| 91蝌蚪视频在线| 亚洲区成人777777精品|