Везде и давно говорят о ES2015, хоть на дворе уже 2017 год, ещё есть люди не знакомые с этим стандартом. ES2015 принёс много нового в мир JavaScript, что-то подхватилось моментально, что-то продолжает оставаться в тени.

Все мы любим использовать модульный подход ES6, стрелочные функции, let и const, promises, классы, от деструктуризации многие пришли в восторг. Реже упоминается о новом типе Symbol, генераторах, Map, Set, WeakMap, WeakSet, и много чего ещё.

На мой взгляд основная причина обильного использования одних нововведений, и крайне редкое использование других — это практическая необходимость. Я не могу вспомнить случая, когда мне бы пригодилось использовать Map вместо обычных массивов или объектов. Генераторы я нахожу полезными и классно что в JS их добавили, но опять же, в моей практике написания Front-End'а, я если и находил им применение, то не чаще чем пару раз. Быть может я просто не изменил мышление и использую старые подходы к разработке под маской let, const, arrow function, классов, но поговорить я хочу всё же не об этом.

Несмотря на все эти WeakSet, генераторы и Symbol'ы, на мой взгляд наиболее всех в стороне оказались Reflect API и Proxy объекты. Давайте вспомним о них, а вдруг там есть что-то полезное.

Начнём с того что Reflect это не конструктор. Это как понятно из названия набор методов для работы с объектами подобно объекту Math.

Предлагаю освежить память и кратко пройтись по некоторым методам Reflect API:

  • Reflect.apply(target, this, args)

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

    function countAmountScore(audienceScore, juryScore) {
       return this.userScore + audienceScore + juryScore;
    }
    let subZeroRating = Reflect.apply(countAmountScore, {userScore: 15}, [5, 10]); // выведет 30

  • Reflect.construct(constructor, args, prototype)

    С помощью этого метода можно вызвать конструктор без использования оператора new. Главной «плюшкой» использования этого метода на мой взгляд является третий, необязательный параметр. Это указание прототипа к обращаемой функции-конструктору.

    function checkFullName(name, surname) {
        this.name = name;
        this.surname = surname;
        this.getName = function() {
          return `${this.name} ${this.middleName} ${this.surname}`
        };
    }
    
    function addMiddleName () {}
    
    addMiddleName.prototype.middleName = 'Ильич';
    
    let Lenin = Reflect.construct(checkFullName, ['Владимир', 'Ленин'], addMiddleName);
    Lenin.getName(); // выведет "Владимир Ильич Ленин"

  • Reflect.defineProperty(object, property, attributes)

    Этот метод действует аналогично одноименному методу у Object, с той лишь разницей, что метод Reflect.defineProperty возвращает булевое значение, в зависимости от успеха или неудачи присвоения.

    Первым аргументом функции идёт объект к которому идёт присвоение свойства, вторым — имя свойства, третьим — опции или просто значение присваиваемого свойства.

  • Reflect.deleteProperty(target, property)

    Метод для удаления свойство и других сущностей, работает аналогично delete, с той лишь разницей, что возвращает булевое значение в зависимости от успеха или неудачи удаления.

  • Reflect.enumerate(object)

    Этот метод не рекомендуется использовать, т.к. он является «устаревшим» (obsolete), если верить документации, но в некоторых браузерах всё ещё может работать.

    Этот метод работает подобно for..in, но вместо того чтобы проходится по объекту — возвращает итерацию, проще говоря — генератор. Но так как многие интерпретаторы этот метод не понимают — рассматривать его нет смысла.

  • Reflect.get(object, property, this)

    Метод служит для получения свойства объекта, применим к массивам.

    // Object
    var obj = { x: 1, y: 2 };
    Reflect.get(obj, 'x'); // 1
    
    // Array
    Reflect.get(['zero', 'one'], 1); // "one"
    
    // Proxy with a get handler
    var x = {p: 1};
    var obj = new Proxy(x, {
      get(t, k, r) { return k + 'bar'; }
    });
    Reflect.get(obj, 'foo'); // "foobar"

  • Reflect.set(object, property, value, this)

    Работает аналогично методу Reflect.get с той лишь, что задаёт указанное значение, а не получает его.

    Метод Reflect.set возвращает булевое значение в зависимости от успеха или неудачи присвоения значения.

  • Reflect.has(object, property)

    Нужен для проверки существования свойства в объекте. Возвращает булевое значение.

    let building = {
        __proto__: {
            type: "castle"
        },
        age: 300
    };
    console.log(Reflect.has(building, "age"));  // выведет true
    console.log(Reflect.has(building, "type"));  // выведет true

  • Reflect.ownKeys(object)

    Метод Reflect.ownKeys — возвращает ключи элементов объекта, в виде массива. Наследуемые свойства игнорируются.

    let obj = {
      name: "Иван Федорович Крузенштерн",
      rank: "Адмирал",
      __proto__: {
        yearOfBirth: 1770
      }
    }
    
    let objKeys = Reflect.ownKeys(obj);
    console.log(objKeys.length);  // выведет 2
    console.log(objKeys[0]);  // выведет name
    console.log(objKeys[1]);  // выведет rank
    

В статье были приведены не все методы из Reflect.API, остальные показались мне почти целиком аналогичными одноименным методам собрата Object.

Положительный герой или отрицательный этот Reflect.API, есть ли в нём что-то полезное — каждый определит сам. Определённо этот программный интерфейс имеет право на жизнь.
Поделиться с друзьями
-->

Комментарии (13)


  1. token
    13.02.2017 14:52
    +2

    И в чем смысл копипаста документации, тем более «В статье были приведены не все методы из Reflect.API»?


    1. CodeViking
      13.02.2017 15:02

      Эта статья, как бы это не звучало — написана больше для личного опыта. Это моя первая публикация, так сказать «заочное выступление» вообще. Я ожидал подобной критики, сам понимаю что революционного ничего я не вещаю. В качестве предлога о чём написать — я решил напомнить людям о такой вещи, которая может быть не особо полезна, но под час может весьма пригодится.

      Понять или осудить мой поступок — ваше право. Я заинтересовался этим API, решил заодно попрактиковать себя как публициста, в качестве первого материала, выдал краткое напоминание об Reflect API в повествовательной форме со своими мыслями. Постараюсь следующие материалы делать интереснее и даст Бог — полезнее.


      1. Aingis
        13.02.2017 16:05
        +2

        За справкой о методах проще зайти на MDN. Вы бы хоть написали, что это такое и где может пригодиться. Пользы было бы больше на порядок.


        1. f0rk
          13.02.2017 16:14

          Может пригодиться для всяческих observable структур данных, rpc и прочего метапрограммирования.


        1. CodeViking
          13.02.2017 16:22

          Благодарю за критику, согласен, всегда за справкой нужно идти на оф.сайт.
          Что это такое вкратце — я сказал.

          Начнём с того что Reflect это не конструктор. Это как понятно из названия набор методов для работы с объектами подобно объекту Math.
          Т.е. набор методов, которые в основном взаимодействуют с объектами, а часто работают просто обёрткой одноимённых методов у Object. Где это применять — дело программиста.


  1. Veikedo
    13.02.2017 16:19
    +1

    Это пригодится для получения типов параметров, при использовании typescript.


    const deps = Reflect.getMetadata("design:paramtypes", constructor).map(type => type.name)


    Как-то писал об этом https://habrahabr.ru/post/305828/


    1. staticlab
      14.02.2017 05:31

      Только в ES 2015 этот метод не декларирован, да и вообще, proposal на Reflect Metadata API ещё даже не в stage 0.


      1. Veikedo
        14.02.2017 05:45

        Верно. Поэтому работает пока что только за флагами транспайлера


  1. Riim
    13.02.2017 16:45
    +1

    Очень не хватает объяснения зачем он нужен этот Reflect, это очень неочевидный момент, тк. большую часть того, что он умеет, на первый взгляд, можно делать и без него. А Proxy забыли не потому, что он малополезен, а потому, что не полифилится ни в рантайме, ни на этапе обработки кода каким-нибудь бабелем. В тоже время на мой взгляд это пожалуй самая интересная фича ES2015 и после появления во всех актуальных браузерах применение ей точно найдётся.


  1. AndreyRubankov
    13.02.2017 16:48
    +2

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

    function printText(text) {
        console.log(text);
    }
    
    // Reflection API:
    Reflect.apply(printName, {}, ["Hello World"]);
    
    // Native:
    printName.apply({}, ["Hello World"]);
    


    Было бы здорово услышать, в чем принципиальная разница и как лучше по-вашему мнению и почему.


    1. urrri
      13.02.2017 20:18

      При изучении этого API, у меня возникло ощущение, что это попытка перенести весь мета-функционал в одно место и, со временем, объявить его deprecated в оригиналах. Мне кажется, это связано с новым ОО подходом, поскольку переопределение методов (в том числе статических) в наследниках может перекрывать оригинальный функционал. И, дабы убрать именной «мусор» из базисных классов, был создан отдельный API.


    1. staticlab
      14.02.2017 05:18

      Кстати разница в том, что в Function.prototype.apply можно передать null/undefined, а в Reflect.apply — нет.


  1. gearbox
    14.02.2017 12:05

    К сожалению оригинал лежит но братья китайцы заботливо сохранили копию, пусть здесь полежит 6 compelling use cases for ES6 proxies