Получить имя класса экземпляра класса ES6


Существуют ли какие-либо "гармоничные" способы получить имя класса из экземпляра класса ES6? Кроме

someClassInstance.constructor.name

В настоящее время я рассчитываю на реализацию Traceur. И похоже, что у Бабеля есть полифилл для Function.name, а у трейсера-нет.

подводя итог: в ES6/ES2015/Harmony не было другого пути, и ничего не ожидается ATM в ES.Следующий.

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

Babel использует core-js на полифилл Function.name, он должен быть загружен вручную для приложений Traceur и TypeScript по мере необходимости.

3 108

3 ответа:

someClassInstance.constructor.name это совершенно правильный способ сделать это. Транспиляторы могут не поддерживать это, но это стандартный способ в соответствии со спецификацией. (Свойство name функций, объявленных через ClassDeclaration productions, задается в 14.5.15, Шаг 6.)

Как говорит @ Domenic, используйте someClassInstance.constructor.name. @Esteban упоминает в комментариях, что

someClassInstance.constructor это функция. Все функции имеют свойство name...

Таким образом, чтобы получить статический доступ к имени класса, выполните следующие действия (это работает с моей версией Babel кстати. Согласно комментариям на @Domenic ваш пробег может отличаться).

class SomeClass {
  constructor() {}
}

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

Обновить

Вавилон был прекрасен, но уродство/минификация в конечном итоге вызвали у меня проблемы. Я делаю игру, и я играю. создание хэша Объединенных ресурсов Спрайта (где ключом является имя функции). После минификации каждая функция / класс получила имя t. Это убивает гашиш. Я использую Gulp в этом проекте, и после чтения gulp-uglify docs я обнаружил, что есть параметр, который предотвращает искажение имени этой локальной переменной/функции. Итак, в моем gulpfile я изменил

.pipe($.uglify()) to .pipe($.uglify({ mangle: false }))

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

Обновить

После обсуждения в комментариях кажется, что уклонение от конвенции .name в пользу определения этих функций является хорошей парадигмой. Это займет всего несколько минут. строки кода, и позволит полностью минимизировать и обобщить ваш код (если он используется в библиотеке). Поэтому я думаю, что передумаю и буду вручную определять getClassName на своих классах. Спасибо @estus!. Геттер / сеттеры обычно являются хорошей идеей по сравнению с прямым переменным доступом в любом случае, особенно в клиентском приложении.
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)

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

Предыдущие ответы объясняли, что someClassInstance.constructor.name работает просто отлично, но если вам нужно программно преобразовать имя класса в строку и не хотите создавать экземпляр только для этого, помните, что:
typeof YourClass === "function"

И, поскольку каждая функция имеет свойство name, Еще один хороший способ получить строку с именем класса-это просто сделать:

YourClass.name
То, что следует ниже, является хорошим примером того, почему это полезно.

Загрузка web компоненты

Как учит нас документация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());
}