Все мы любим использовать модульный подход 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)
Veikedo
13.02.2017 16:19+1Это пригодится для получения типов параметров, при использовании typescript.
const deps = Reflect.getMetadata("design:paramtypes", constructor).map(type => type.name)
Как-то писал об этом https://habrahabr.ru/post/305828/
Riim
13.02.2017 16:45+1Очень не хватает объяснения зачем он нужен этот Reflect, это очень неочевидный момент, тк. большую часть того, что он умеет, на первый взгляд, можно делать и без него. А Proxy забыли не потому, что он малополезен, а потому, что не полифилится ни в рантайме, ни на этапе обработки кода каким-нибудь бабелем. В тоже время на мой взгляд это пожалуй самая интересная фича ES2015 и после появления во всех актуальных браузерах применение ей точно найдётся.
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"]);
Было бы здорово услышать, в чем принципиальная разница и как лучше по-вашему мнению и почему.urrri
13.02.2017 20:18При изучении этого API, у меня возникло ощущение, что это попытка перенести весь мета-функционал в одно место и, со временем, объявить его deprecated в оригиналах. Мне кажется, это связано с новым ОО подходом, поскольку переопределение методов (в том числе статических) в наследниках может перекрывать оригинальный функционал. И, дабы убрать именной «мусор» из базисных классов, был создан отдельный API.
staticlab
14.02.2017 05:18Кстати разница в том, что в Function.prototype.apply можно передать null/undefined, а в Reflect.apply — нет.
gearbox
14.02.2017 12:05К сожалению оригинал лежит но братья китайцы заботливо сохранили копию, пусть здесь полежит 6 compelling use cases for ES6 proxies
token
И в чем смысл копипаста документации, тем более «В статье были приведены не все методы из Reflect.API»?
CodeViking
Эта статья, как бы это не звучало — написана больше для личного опыта. Это моя первая публикация, так сказать «заочное выступление» вообще. Я ожидал подобной критики, сам понимаю что революционного ничего я не вещаю. В качестве предлога о чём написать — я решил напомнить людям о такой вещи, которая может быть не особо полезна, но под час может весьма пригодится.
Понять или осудить мой поступок — ваше право. Я заинтересовался этим API, решил заодно попрактиковать себя как публициста, в качестве первого материала, выдал краткое напоминание об Reflect API в повествовательной форме со своими мыслями. Постараюсь следующие материалы делать интереснее и даст Бог — полезнее.
Aingis
За справкой о методах проще зайти на MDN. Вы бы хоть написали, что это такое и где может пригодиться. Пользы было бы больше на порядок.
f0rk
Может пригодиться для всяческих observable структур данных, rpc и прочего метапрограммирования.
CodeViking
Благодарю за критику, согласен, всегда за справкой нужно идти на оф.сайт.
Т.е. набор методов, которые в основном взаимодействуют с объектами, а часто работают просто обёрткой одноимённых методов у Object. Где это применять — дело программиста.Что это такое вкратце — я сказал.