Отримайте назву екземпляра класу ES6


157

Чи є якісь "гармонійні" способи отримати ім'я класу з екземпляра класу ES6? Окрім

someClassInstance.constructor.name

В даний час я розраховую на реалізацію Traceur. І, схоже, у Function.nameБабелі є поліфіл, поки у Трасера ​​немає.

Підводячи підсумок: у ES6 / ES2015 / Гармонія іншого шляху не було, і банкоматів у ES.Next не очікується.

Він може надавати корисні зразки для немініфікованих додатків на сервері, але небажаний у додатках, призначених для браузера / настільних ПК / мобільних пристроїв.

Babel використовуєcore-js для полізаповнення Function.name, його слід завантажувати вручну для програм Traceur і TypeScript, якщо потрібно.


2
Я натрапив на те саме питання для Traceur єдиним рішенням було проаналізувати власне код класу для вилучення імені, яке, на мою думку, не вважається гармонійним. Я просто ковтнув таблетку і перейшов на Вавілон; Розвиток Traceur здається дещо застійним, і багато функцій ES6 погано реалізовані. Як ви вже згадували, instance.constructor.nameі class.nameповерніть ім'я класу у відповідному ES6.
Андрій Одрі

Здається, це єдиний спосіб.
Фредерік Краутвальд

Це в стандарті ES6?
друдру

12
Варто згадати, що someClassInstance.constructor.nameвас заблудять, якщо ви зведете код.
JamesB

stackoverflow.com/questions/2648293/… Можливо, хочете подивитися на це, чи варто працювати з / п .constructor.
Флорі

Відповіді:


206

someClassInstance.constructor.nameце саме правильний спосіб зробити це. Транспілери можуть не підтримувати це, але це стандартний спосіб відповідно до специфікації. ( nameВластивість функцій, задекларованих за допомогою ClassDeclaration productions, встановлено в 14.5.15 , крок 6.)


2
Ось чого я боявся. Чи знаєте ви про будь-який розумний поліфіл для цього? Я намагався розібратися, як це робить Бабел, але успіху мав дуже мало.
колба Естуса

Я насправді не знаю, що ви розумієте під полізаливом для мовної функції (класи).
Доменік

Я маю на увазі поліфіл для constructor.name. Здається, що Babel це реалізував, але мені не вдалося зрозуміти, як це відбувається.
колба Естуса

2
@estus someClassInstance.constructor- це функція. Усі функції мають nameвластивість, встановлене на його ім'я. Ось чому дівочій нічого не потрібно робити. Перегляньте сторінку developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Esteban

2
@Esteban Схоже, що Babel наполегливо рекламує полі-заповнення core-js (включаючи polyfill для Function.name), тому деякі збірки Babel можуть працювати з вікна у всіх браузерах.
колба Естуса

52

Як говорить @Domenic, використовуйте someClassInstance.constructor.name. @Esteban в коментарях згадує, що

someClassInstance.constructorє функцією. Усі функції мають nameвластивість ...

Таким чином, для доступу до назви класу статично, зробіть наступне (це працює з моєю версією Babel BTW. Відповідно до коментарів до @Domenic ваш пробіг може відрізнятися).

class SomeClass {
  constructor() {}
}

var someClassInstance = new SomeClass();
someClassInstance.constructor.name;      // === 'SomeClass'
SomeClass.name                           // === 'SomeClass'

Оновлення

Вавілон був чудовий, але зникнення / мінімізація все-таки спричинило у мене проблеми. Я створюю гру і створюю хеш об'єднаних ресурсів Sprite (де ключовим є ім'я функції). Після мінімізації кожну функцію / клас було названо t. Це вбиває хеш. Я використовую Gulpв цьому проекті, і після прочитання документів gulp-uglify я виявив, що існує параметр, який запобігає тому, щоб ця локальна змінна / ім'я функції не відбувалася. Отже, у своєму gulpfile я змінився

.pipe($.uglify()) до .pipe($.uglify({ mangle: false }))

Тут є компроміс продуктивності та читабельності. Якщо не керувати іменами, це призведе до (трохи) більшого файлу збірки (більше мережевих ресурсів) та потенційно повільніше виконання коду (потрібне цитування - може бути BS). З іншого боку, якби я зберігав це однаково, мені доведеться вручну визначати getClassNameдля кожного класу ES6 - на рівні статики та екземпляра. Ні, дякую!

Оновлення

Після обговорення в коментарях здається, що уникнення .nameконвенції на користь визначення цих функцій є хорошою парадигмою. Це займе лише кілька рядків коду, і дозволить повністю мінімізувати та загальнокоректувати ваш код (якщо він використовується в бібліотеці). Тож я думаю, що передумав і вручну визначуся getClassNameна своїх заняттях. Дякую @estus! . Як правило, Getter / Setters є хорошою ідеєю порівняно з прямим змінним доступом у будь-якому разі, особливо у клієнтській програмі.

class SomeClass {
  constructor() {}
  static getClassName(){ return 'SomeClass'; }
  getClassName(){ return SomeClass.getClassName(); }
}
var someClassInstance = new SomeClass();
someClassInstance.constructor.getClassName();      // === 'SomeClass' (static fn)
someClassInstance.getClassName();                  // === 'SomeClass' (instance fn)
SomeClass.getClassName()                           // === 'SomeClass' (static fn)

3
Повністю вимкнути функцію керування - це не дуже гарна ідея, оскільки манглінг багато сприяє мінімізації. Не дуже гарна ідея використовувати спочатку тему в коді клієнта, але класи можна захистити від reservedкерування за допомогою параметра Uglify (список класів можна отримати за допомогою regexp або будь-якого іншого).
колба Естуса

Дуже правильно. Існує компроміс з більшим розміром файлу. Схоже, саме так ви можете використовувати RegEx для запобігання керуванню лише вибраними елементами . Що ви маєте на увазі, що "не дуже гарна ідея або використовувати тему в коді клієнта"? Чи представляв би це ризик для безпеки в деяких сценаріях?
Джеймс Л.

1
Ні, просто те, що вже було сказано. Це звичайно, щоб JS на стороні клієнта був мінімізований, тому вже відомо, що ця модель спричинить проблеми для програми. Додатковий один рядок коду для ідентифікатора рядка класу замість акуратного nameшаблону може бути просто безпрограшним. Те саме можна застосувати і до Вузла, але в меншій мірі (наприклад, затуманений додаток Electron). Як правило, я б покладався на nameкод сервера, але не на код браузера і не в загальній бібліотеці.
колба Естуса

Гаразд, тому ви рекомендуєте вручну визначити 2 функції getClassName (статичні та екземпляр), щоб обійти пекельний хід і дозволити повне мінімізацію (без дратівливого RegEx). Цей пункт про бібліотеку має багато сенсу. Має багато сенсу. Для мене мій проект - це саморобний невеликий додаток Кордова, так що це насправді не проблеми. Щось поза цим, я можу бачити, як ці проблеми піднімаються. Дякую за обговорення! Якщо ви можете придумати будь-які вдосконалення публікації, сміливо відредагуйте її.
Джеймс Л.

1
Так, я спочатку використовував nameдля DRYer-коду для вилучення імені об'єкта, який використовує клас (послуга, плагін тощо) з імені класу, але врешті-решт я виявив, що він явно дублює його статичною опорою ( id, _name) це просто найбільш солідний підхід. Хорошою альтернативою є небайдужість до імені класу, використання самого класу (об’єкта функції) як ідентифікатора для сутності, яка відображається до цього класу, і importде завгодно (підхід, який використовував Angular 2 DI).
колба Естуса

8

Отримання назви класу безпосередньо з класу

Попередні відповіді пояснювали, що це someClassInstance.constructor.nameпрацює чудово, але якщо вам потрібно програмно перетворити ім’я класу в рядок і не хочете створювати екземпляр лише для цього, пам’ятайте, що:

typeof YourClass === "function"

Оскільки кожна функція має nameвластивість, ще один приємний спосіб отримати рядок з назвою вашого класу - це просто зробити:

YourClass.name

Далі - хороший приклад, чому це корисно.

Завантаження веб-компонентів

Як навчає нас документація MDN , так ви завантажуєте веб-компонент:

customElements.define("your-component", YourComponent);

Звідки YourComponentпоходить клас HTMLElement. Оскільки вважається хорошою практикою називати клас вашого компонента за самим тегом компонента, було б непогано написати допоміжну функцію, яку всі ваші компоненти могли використовувати для реєстрації. Отже ось ця функція:

function registerComponent(componentClass) {
    const componentName = upperCamelCaseToSnakeCase(componentClass.name);
    customElements.define(componentName, componentClass);
}

Отже, все, що вам потрібно зробити:

registerComponent(YourComponent);

Що приємно, оскільки він менш схильний до помилок, ніж самостійно писати тег компонентів. Для завершення цього upperCamelCaseToSnakeCase()функція:

// converts `YourString` into `your-string`
function upperCamelCaseToSnakeCase(value) {
    return value
        // first char to lower case
        .replace(/^([A-Z])/, $1 => $1.toLowerCase())
        // following upper chars get preceded with a dash
        .replace(/([A-Z])/g, $1 => "-" + $1.toLowerCase());
}

Дякую. Приклад - на стороні клієнта. Як вже було сказано, є деякі проблеми із використанням імен функцій у браузерах. Очікується, що майже кожен фрагмент коду браузера буде мінімізований, але це зруйнує код, який спирається на ім'я функції.
колба Естуса

Так, ви абсолютно праві. Мініфактор повинен бути налаштований так, щоб він не торкався імен класів для цього підходу до роботи.
Лусіо Пайва

1

Для транспіляції вавило (перед мініфікацією)

Якщо ви використовуєте Babel з @babel/preset-env, можна зберегти визначення класів, не перетворюючи їх у функції (що видаляє constructorвластивість)

Ви можете залишити стару сумісність браузера з цією конфігурацією у вашому babel.config / babelrc:

{
  "presets": [
    ["@babel/preset-env", {"targets": {"browsers": ["> 2%"]}}]
  ]
}

Більше інформації про targets: https://babeljs.io/docs/en/babel-preset-env#targets

Для мінімізації вабелів (після транспіляції)

Здається, що зараз немає простого рішення ... Нам потрібно розглянути виключення.


Чи можете ви пояснити далі, як це повинно допомогти в мінімізації? class Foo {}буде впорядковано до чогось подібного class a{}до будь-якої цілі. У Fooмінімізованому вихідному коді не буде жодного слова.
колба Естуса

Чесно кажучи, я не копав більше, ніж документації, і той факт, що це допомогло мені використовувати цю конфігурацію ... Я використовую ECSY в проект, який перекладається на вавило, і для цього потрібні параметри для отримання дійсних імен класів: github.com/MozillaReality/ecsy/isissue/ 119
Ifnot

Я бачу. Це дуже специфічно для коду, яким ви мали справу. Назви імен можуть зберігатися для модулів ES та ES6, оскільки export class Foo{}їх не можна ефективно зменшити, але це може бути різним в інших місцях, ми не можемо знати, як саме, не маючи хорошого уявлення про те, що відбувається з конкретними фрагментами коду під час збирання. У будь-якому випадку це не змінилося з 2015 року. Деякі конфігураційні компіляції та код завжди були можливі. І я б сказав, що ця можливість все ще надто крихка, щоб використовувати назви класів для логіки додатків. Назва класу, на який ви покладаєтесь, може стати сміттям після однієї випадкової зміни вихідного коду
Estus Flask

1
Гаразд, я знайшов те, що відбувається, переглянувши код. Моє рішення фіксує транспіляцію класів у функції. Так це допомагає перед мініфікацією. Але не з проблемою мінімізації. Мені потрібно продовжувати копати, тому що я не міг зрозуміти, як весь мій код із використанням constructor.nameвсе ще працює у мінімізованій версії ... нелогічно: /
Ifnot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.