Получить имя класса экземпляра класса ES6
Есть ли "гармоничные" способы получить имя класса из экземпляра класса ES6? Кроме как
someClassInstance.constructor.name
В настоящее время я рассчитываю на реализацию Traceur. И кажется, что у Бабеля есть полифилл для Function.name
в то время как Traceur нет.
Подводя итог, можно сказать, что в ES6/ES2015/Harmony другого пути не было, а в ES.Next ничего не ожидается.
Он может предоставлять полезные шаблоны для незавершенных серверных приложений, но нежелателен в приложениях, предназначенных для браузера / настольного компьютера / мобильного устройства.
Бабель использует core-js
чтобы заполнить Function.name
, он должен быть загружен вручную для приложений Traceur и TypeScript в зависимости от ситуации.
5 ответов
someClassInstance.constructor.name
это совершенно правильный способ сделать это. Транспортеры могут не поддерживать это, но это стандартный способ согласно спецификации. (Свойство name функций, объявленных через производства ClassDeclaration, устанавливается в 14.5.15, шаг 6.)
Как говорит @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, Метод получения / установки обычно является хорошей идеей по сравнению с прямым доступом к переменным в любом случае, особенно в клиентском приложении.
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
Далее следует хороший пример того, почему это полезно.
Загрузка веб-компонентов
Как учит нас документация 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());
}
Для транспиляции бабеля (до минификации)
Если вы используете Babel с @babel/preset-env
, можно сохранить определения классов, не преобразовывая их в функции (что удаляет constructor
свойство)
Вы можете отказаться от некоторой совместимости старого браузера с этой конфигурацией в своем babel.config / babelrc
:
{
"presets": [
["@babel/preset-env", {"targets": {"browsers": ["> 2%"]}}]
]
}
Больше информации о
targets
: https://babeljs.io/docs/en/babel-preset-env
Для минификации бабеля (после транспиляции)
Похоже, сейчас нет простого решения... Нам нужно посмотреть, как искажать исключения.
Если у вас есть контроль над реализацией класса
Я добавляю это здесь, так как это была вершина Google, когда я пытался понять, как это сделать.
Кажется, это работает для меня в node.
./Суперкласс.js
'use strict';
/**
* A super class
*
* To show that it works with inheritance too
*
* In my usage, I'm also extending the Error class
*/
module.exports = class SuperClass extends Error {
constructor(message, options = {}) {
super(message);
this.name = this.constructor.name;
// ...other shared construction functionality
}
};
./Подкласс.js
'use strict';
const SuperClass = require('./SuperClass');
/**
* a sub class
*/
module.exports = class SubClass extends SuperClass {
constructor(message, options = {}) {
super(message, options);
// ... specific sub class construction functionality
}
};
SubClass.name; // -> 'SubClass'
let instance = new SubClass('message');
instance.name; // -> 'SubClass'
Очевидно, опять же, это работает только с классами, над которыми у вас есть полный контроль над реализацией.
Я бы, наверное, изменил его на_name
или что-то еще, но это должно привести его в соответствие с другой библиотекой, которую я использую.