Краткое руководство по декораторам JavaScript

внешний интерфейс JavaScript TypeScript React.js

Декораторы — это новая функция JavaScript, добавленная в ES7. Учащиеся, знакомые с Typescript, должны ознакомиться с этой функцией раньше, поскольку ранее TypeScript уже поддерживал использование декораторов и обеспечивал поддержку ES5. Эта статья подробно расскажет о декораторах, я уверен, вы почувствуете удобство и элегантность, которые они привносят в программирование.

Я был профессиональным .NET-программистом до того, как стал специализироваться на интерфейсной разработке, и я очень хорошо знаком с использованием «функций» в .NET. Напишите скобки для класса, метода или свойства и инициализируйте функцию внутри скобок, которая повлияет на поведение класса, метода или свойства. Это особенно полезно в АОП-программировании и структурах ORM, таких как магия. Но в то время в JavaScript не было такой функции. Первое использование декораторов в TypeScript связано с тем, что мы хотим сериализовать контекстную информацию всего приложения, и нам нужен простой способ поставить метку на исходную модель предметной области, чтобы определить, будет ли она сериализована или нет. проявить свою силу в этом сценарии. Позже нам нужно реорганизовать наше управление состоянием, чтобы преобразовать между определениями изменяемых классов и неизменяемыми объектными приложениями.Если мы используем декораторы, как с точки зрения удобства редактирования, так и с точки зрения разделения, мы получили удивительные результаты. Я всегда хотел разобраться в связанном использовании декораторов в популярном документе и использовать самый простой способ объяснить эту тему, но я еще не написал. Случайно нашла в интернете статью (https://cabbageapps.com/fell-love-js-decorators/), текст этой статьи точно соответствует тому, что я хочу выразить, поэтому взял на переработку и адаптация. Студенты, которые любят читать по-английски, могут щелкнуть ссылку, чтобы прочитать исходный текст.

1.0 Шаблон декоратора

Если мы будем напрямую искать «декораторы» или «декораторы» в поисковой системе и результатах, связанных с программированием, мы увидим введение шаблона декоратора в шаблон проектирования.

Более наглядный пример выглядит следующим образом:

WeaponAccessory на изображении выше — это декоратор, добавляющий базовому классу дополнительные методы и знакомые элементы. Если вы этого не понимаете, ничего страшного, следуйте за мной шаг за шагом, чтобы реализовать свой собственный декоратор, и вы поймете это естественным образом. Следующая картинка может помочь вам интуитивно понять декоратор.

5.gif

Мы просто понимаем декоратор, его можно рассматривать как некую упаковку, привычную упаковку объектов, методов. Когда нам нужно получить доступ к объекту, если мы получим к нему доступ через оболочку вокруг объекта, будет запущено поведение, прикрепленное к оболочке. Например, пистолет с глушителем. Глушитель является украшением, но он интегрирован с оригинальным пистолетом и работает при выстреле из пистолета.

Эта концепция хорошо понятна с объектно-ориентированной точки зрения. Так как же использовать декораторы в JavaScript?

1.1 Начните свой путь декоратора

Декораторы — это новая функция, поддерживаемая только ES7, но с Babel и TypesScript мы можем использовать ее уже сейчас.В этой статье в качестве примера используется TypesScript.

Сначала измените файл tsconfig.json, установите для ExperimentDecorators и emitDecoratorMetadata значение true.

{
  "compilerOptions": {
    "target": "es2015",
    "module": "commonjs",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true
  },
  "exclude": [
    "node_modules",
  ]
}

Мы начинаем с эффекта, а затем анализируем его слой за слоем. Взгляните на следующий фрагмент кода:

function leDecorator(target, propertyKey: string, descriptor: PropertyDescriptor): any {
    var oldValue = descriptor.value;

    descriptor.value = function() {
      console.log(`Calling "${propertyKey}" with`, arguments,target);
      let value = oldValue.apply(null, [arguments[1], arguments[0]]);

      console.log(`Function is executed`);
      return value + "; This is awesome";
    };

    return descriptor;
  }

  class JSMeetup {
    speaker = "Ruban";
    //@leDecorator
    welcome(arg1, arg2) {
      console.log(`Arguments Received are ${arg1} ${arg2}`);
      return `${arg1} ${arg2}`;
    }
  }

  const meetup = new JSMeetup();

  console.log(meetup.welcome("World", "Hello"));

Запуск приведенного выше кода дает следующие результаты:

Давайте изменим код ниже, чтобы раскомментировать строку 17.

Запустив код снова, результат выглядит следующим образом:

Обратите внимание на вывод слева на изображении выше и номера строк, показанные справа. Теперь мы можем быть уверены, что после добавления тега @leDecorator поведение функции приветствия изменилось, и место, которое запускает изменение, — это функция leDecorator. Согласно нашему базовому пониманию декораторов, описанному выше, мы можем думать, что leDecorator — это декоратор приветствия.Декораторов и деконов связывает @符.

На уровне JavaScript мы уже узнали о декораторах перцептивно, и наш код украшает функцию. В JavaScript есть 4 типа декораторов:

  • Функция декоратора метода Decorator
  • Декораторы свойств Познакомьтесь с декораторами
  • Декоратор класса Декоратор класса
  • Декоратор параметров Декоратор параметров

Давайте сломаем это один за другим! Ну давай же!

1.2 Декораторы функций

Первый декоратор, который будет взломан, — это декоратор функции.Этот раздел является основным содержанием этой статьи.Мы получим представление о сущности декораторов JavaScript, объяснив декоратор функции.

Используя декораторы функций, мы можем управлять вводом и выводом функций.

Вот определение декоратора функции:

MethodDecorator = <T>(target: Object, key: string, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | Void;

Пока мы следуем приведенному выше определению, мы можем настроить декоратор функции.Значение трех параметров следующее:

  • цель -> украшенный объект
  • ключ -> украшенное имя функции
  • descriptor -> Дескриптор свойства переданного свойства.Вы можете просмотреть дескриптор свойства с помощью метода Object.getOwnPropertyDescriptor().

Дополнительные сведения об дескрипторах атрибутов см. на странице https://www.jianshu.com/p/19529527df80.

Короче говоря, дескрипторы свойств можно использовать для настройки возвращаемого значения, поведения получения/установки свойства объекта, возможности его удаления, изменения и возможности перечисления. Чтобы вы без труда разобрались с декораторами, давайте рассмотрим наглядный пример.

Откройте консоль браузера и введите следующий код:

var o, d;
var o = { get foo() { return 17; }, bar:17, foobar:function(){return "FooBar"} };

d = Object.getOwnPropertyDescriptor(o, 'foo');
console.log(d);
d = Object.getOwnPropertyDescriptor(o, 'bar');
console.log(d);
d = Object.getOwnPropertyDescriptor(o, 'foobar');
console.log(d);

Результат выглядит следующим образом:

Здесь мы определяем объект o, определяем три свойства — foo, bar и foobar, а затем получаем дескриптор каждого свойства через Object.getOwnPropertyDescriptor() и распечатываем его. Ниже мы кратко опишем value , enumerable , configurable и writable .

  • value -> буквальное значение или возвращаемое значение после вычисления функции/свойства.
  • enumerable -> можно перечислить (можно ли перечислить в цикле (for x in obj))
  • configurable -> можно ли настроить свойство
  • writable -> Доступно ли свойство для записи.

Каждое свойство или метод имеет свой собственный дескриптор, с помощью которого мы можем изменить поведение или возвращаемое значение свойства. Вот ключ:

Суть декоратора заключается в модификации дескриптора

Пора начинать писать декоратор.

Метод декоратора примера 1.2.1

Затем мы используем декораторы методов для изменения ввода и вывода функции.

function leDecorator(target, propertyKey: string, descriptor: PropertyDescriptor): any {
    var oldValue = descriptor.value;

    descriptor.value = function() {
      console.log(`Calling "${propertyKey}" with`, arguments,target);
      // Executing the original function interchanging the arguments
      let value = oldValue.apply(null, [arguments[1], arguments[0]]);
      //returning a modified value
      return value + "; This is awesome";
    };

    return descriptor;
  }

  class JSMeetup {
    speaker = "Ruban";
    //@leDecorator
    welcome(arg1, arg2) {
      console.log(`Arguments Received are ${arg1}, ${arg2}`);
      return `${arg1} ${arg2}`;
    }
  }

  const meetup = new JSMeetup();

  console.log(meetup.welcome("World", "Hello"));

Когда декоратор не используется, выходное значение:

Arguments Received are World, Hello
World Hello

Когда декоратор включен, выходное значение:

Calling "welcome" with { '0': 'World', '1': 'Hello' } JSMeetup {}
Arguments Received are Hello, World
Hello World; This is awesome

Мы видим, что выходное значение метода изменилось. Теперь посмотрим на декоратор метода, который мы определили.Через параметры leDecorator получает имя вызывающего объекта, параметры декорируемого метода и дескриптор декорируемого метода при его выполнении. Во-первых, исходное значение дескриптора метода, который является определенным нами методом приветствия, сохраняется через переменную oldValue. Затем переназначается descriptor.value.

В новой функции сначала вызывается исходная функция, получается возвращаемое значение, а затем возвращаемое значение модифицируется. Наконец, верните дескриптор, новый дескриптор будет применен к методу приветствия, а тело встроенной функции было заменено.

Используя декораторы, мы реализуем оболочку вокруг исходной функции, которая может изменять входные и выходные данные метода, что означает, что мы можем применять все виды желаемых магических эффектов к целевому методу.

Вот несколько замечаний:

  • Декораторы выполняются при объявлении класса, а не при его создании.
  • Декоратор метода возвращает значение
  • Мы рекомендуем сохранять старый дескриптор и возвращать новый, что очень полезно в приложениях с несколькими дескрипторами.
  • При установке значения дескриптора не используйте стрелочные функции.

Теперь мы закончили и разобрались с декоратором первого метода. Далее мы подходим к декоратору школьного имущества.

1.3 Декораторы атрибутов

Деловые декораторы похожи на метод декораторы. Через свойства декораторы мы можем переопределить Getter, Setters и модифицирующие свойства, такие как перечисленные и настраиваемые.

Декоратор свойств определяется следующим образом:

PropertyDecorator = (target: Object, key: string) => void;

Описание параметра выглядит следующим образом:

  • Цель: владелец недвижимости
  • ключ: имя атрибута

Прежде чем конкретно использовать декоратор свойств, давайте кратко рассмотрим метод Object.defineProperty. Метод Object.defineProperty обычно используется для динамического добавления или изменения свойств объекта. Вот пример:

var o = { get foo() { return 17; }, bar:17, foobar:function(){return "FooBar"} };

Object.defineProperty(o, 'myProperty', {
get: function () {
return this['myProperty'];
},
set: function (val) {
this['myProperty'] = val;
},
enumerable:true,
configurable:true
});

Протестируйте приведенный выше код в консоли отладки.

Из результатов видно, что с помощью Object.defineProperty мы динамически добавляем свойства к объекту. Ниже мы реализуем простой декоратор свойств на основе Object.defineProperty.

function realName(target, key: string): any {
    // property value
    var _val = target[key];

    // property getter
    var getter = function () {
      return "Ragularuban(" + _val + ")";
    };

    // property setter
    var setter = function (newVal) {
      _val = newVal;
    };

    // Create new property with getter and setter
    Object.defineProperty(target, key, {
      get: getter,
      set: setter
    });
  }

  class JSMeetup {
    //@realName
    public myName = "Ruban";
    constructor() {
    }
    greet() {
      return "Hi, I'm " + this.myName;
    }
  }

  const meetup = new JSMeetup();
  console.log(meetup.greet());
  meetup.myName = "Ragul";
  console.log(meetup.greet());

Когда декоратор не применяется, вывод:

Hi, I'm Ruban
Hi, I'm Ragul

После включения декоратора результат такой:

Hi, I'm Ragularuban(Ruban)
Hi, I'm Ragularuban(Ragul)

Это очень просто? Далее идет декоратор класса.

1.4 Декоратор класса

Декоратор класса предназначен для динамического добавления и изменения связанных свойств и методов класса с помощью конструктора класса. Вот определение декоратора класса:

ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction;

ClassDecorator принимает только один параметр, который является конструктором класса. Следующий пример кода изменяет исходный атрибут спикера класса и динамически добавляет дополнительный атрибут.

function AwesomeMeetup<T extends { new (...args: any[]): {} }>(constructor: T) {
    return class extends constructor implements extra {
      speaker: string = "Ragularuban";
      extra = "Tadah!";
    }
  }

  //@AwesomeMeetup
  class JSMeetup {
    public speaker = "Ruban";
    constructor() {
    }
    greet() {
      return "Hi, I'm " + this.speaker;
    }
  }

  interface extra {
    extra: string;
  }

  const meetup = new JSMeetup() as JSMeetup & extra;
  console.log(meetup.greet());
  console.log(meetup.extra);

Выходное значение без включенного декоратора:

При включенном декораторе вывод:

Здесь следует отметить, что,Конструктор будет вызываться только один раз.

Позвольте мне изучить последний тип декораторов, декоратор параметров.

1.5 Декораторы параметров

Если функция декоратора параметра выводится из декоратора, упомянутого выше, это может быть изменение параметра, но на самом деле это не так. Декораторы параметров часто используются для маркировки специальных параметров, а затем чтения соответствующих меток в декораторе метода для выполнения дальнейших операций. Например:

function logParameter(target: any, key: string, index: number) {
    var metadataKey = `myMetaData`;
    if (Array.isArray(target[metadataKey])) {
      target[metadataKey].push(index);
    }
    else {
      target[metadataKey] = [index];
    }
  }

  function logMethod(target, key: string, descriptor: any): any {
    var originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {

      var metadataKey = `myMetaData`;
      var indices = target[metadataKey];
      console.log('indices', indices);
      for (var i = 0; i < args.length; i++) {

        if (indices.indexOf(i) !== -1) {
          console.log("Found a marked parameter at index" + i);
          args[i] = "Abrakadabra";
        }
      }
      var result = originalMethod.apply(this, args);
      return result;

    }
    return descriptor;
  }

  class JSMeetup {
    //@logMethod
    public saySomething(something: string, @logParameter somethingElse: string): string {
      return something + " : " + somethingElse;
    }
  }

  let meetup = new JSMeetup();

  console.log(meetup.saySomething("something", "Something Else"));

В приведенном выше коде мы определяем декоратор параметров, который помещает оформленные параметры в указанный массив. В декораторе метода ищем отмеченный параметр для дальнейшей обработки Без включенного декоратора вывод будет следующим:

При включенном декораторе вывод выглядит следующим образом:

1.6 Резюме

Теперь, когда мы изучили использование всех декораторов, вот краткое изложение ключевых применений:

  • Ядром декоратора метода является дескриптор метода.
  • Ядром декоратора свойств является Object.defineProperty.
  • Ядром декоратора класса является конструктор
  • Основная роль декоратора параметра заключается в том, чтобы пометить, использовать декоратор метода, чтобы использовать

Чтобы узнать больше хороших статей о внешнем интерфейсе, обратите внимание на номер подписки WeChat «Xuanhun Studio» и ответьте «qd».

玄魂工作室
Вот справочные статьи: https://www.typescriptlang.org/docs/handbook/decorators.html

https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Decorators.md

https://survivejs.com/react/appendices/understanding-decorators/

https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841

https://blog.wolksoftware.com/decorators-metadata-reflection-in-typescript-from-novice-to-expert-part-ii https://github.com/arolson101/typescript-decorators