Как создать экземпляр класса из строки в JavaScript
Я нахожусь в странной ситуации, когда мне нужно создать экземпляр нового класса со строкой, хранящейся в переменной, но даже если я уверен, что имя класса указано правильно, я получаю ошибку, что данное имя класса не является конструктором
Вот фиктивный код, который не работает:
class Foo {
constructor(){
console.log('Foo!');
}
};
const foo = 'Foo';
const bar = new window[foo]();
console.log(bar);
Это Trow эта ошибка:
Uncaught TypeError: window[foo] is not a constructor
3 ответа
Одна из возможностей заключается в использовании eval
,
class Foo {
constructor(){
console.log('Foo!');
}
};
const foo = 'Foo';
const bar = eval(`new ${foo}()`);
console.log(bar);
Вам нужно будет оценить безопасность использования eval()
в ваших конкретных обстоятельствах. Если вы знаете происхождение строки, которую вы вставляете в код, который вы запускаете eval()
или вы можете сначала его продезинфицировать, затем это может быть безопасно.
Я лично предпочел бы таблицу поиска. Если у вас есть известное количество классов, которые вы хотите отобразить по строке, то вы можете создать свою собственную таблицу поиска и использовать ее. Преимущество в том, что непредвиденных последствий не может быть, если в строке есть странные вещи:
class Foo {
constructor(){
console.log('Foo!');
}
};
class Goo {
constructor(){
console.log('Goo!');
}
};
// construct dict object that contains our mapping between strings and classes
const dict = new Map([['Foo', Foo], ['Goo', Goo]]);
// make a class from a string
const foo = 'Foo';
let bar = new (dict.get(foo))()
console.log(bar);
Если вы действительно собираетесь пойти по этому пути, вы можете инкапсулировать его в функцию, а затем добавить обработку ошибок, если строка не найдена в dict
,
Это должно быть лучше, чем использование глобального или Window
объект как ваш механизм поиска по нескольким причинам:
Если я вспомню,
class
Определения в ES6 не помещаются автоматически в глобальный объект, как это было бы с другими объявлениями переменных верхнего уровня (Javascript пытается избежать добавления ненужного мусора поверх ошибок предыдущего проекта).Таким образом, если вы собираетесь вручную назначить объект поиска, вы также можете использовать другой объект и не загрязнять глобальный объект. Вот что
dict
объект используется для здесь.
Есть хорошие решения, но я думаю, что нам нужно немного больше теории
Эти вопросы — лучший пример использования фабричного шаблона.
Шаблоны проектирования могут сделать ваш код более гибким, более устойчивым к изменениям и более простым в обслуживании.
Тео, можно ли использовать фабричные шаблоны в JavaScript?
Да, мой юный падаван.
Но что такое шаблон Factory?
Фабричный шаблон — это творческий шаблон проектирования, что означает, что он имеет дело с созданием объектов. Существует три теоретических типа фабричных шаблонов:
- Простая фабрика
- Заводской метод
- Абстрактная фабрика
Simple Factory — это объект, который инкапсулирует создание другого объекта. В ES6 это может быть конструктор, который создается в функции, а затем возвращает экземпляр следующим образом:
class Player {...}
const PlayerFactory = {
makePlayer: (type, level) => new Player(type, level),
}
В этом примереmakePlayer
возвращает экземпляр класса.
Фабричный метод определяет один метод для создания экземпляра класса, который переопределяется подклассами, которые решают, что возвращать.
В ES6 можно реализовать расширение классов, потому что нет интерфейсов и использовать метод для создания экземпляров и объектов с использованиемnew
class Dragon {...}
class Snake {...}
class Player {
fightMonster() {
const monster = this.makeMonster()
monster.attack()
}
}
class Warrior extends Player {
makeMonster() {
return new Dragon()
}
}
class Knight extends Player {
makeMonster() {
return new Snake()
}
}
В этом примереPlayer
вызов классаmakeMonster
метод тогдаWarrior
иKnight
классы переопределяютmakeMoster
метод для возвратаDragon
илиSnake
экземпляр класса.
Наконец, абстрактная фабрика предоставляет интерфейс для создания семейств связанных или зависимых объектов без указания их конкретных классов.
Интерпретацияfamilies
может быть как категория или список классов. В ES6 это может быть экземпляр, инкапсулирующий группу отдельных фабрик с общей целью. Он отделяет детали реализации набора объектов от их общего использования.
В этом последнем примере мы видим, что функцияclassFactory
использует шаблон Factory Abstract, потому что создает экземпляр класса из списка поддерживаемых ОС (Linux или Windows), устанавливая maker с конструктором желаемого класса, в этом случаеWinButton
сорт.
Похоже на @jfriend00 ...
let className = "Foo";
let dynamicConstructor = {};
dynamicConstructor[className] = class {
constructor(){
console.log('Foo!');
}
};
let fooInstance = new dynamicConstructor[className]();
console.debug(fooInstance);
Также можно использовать своего рода конструктор фабричного класса.
const classFactory = (_className)=>{
let dynamicConstructor = {};
dynamicConstructor[_className] = class {
constructor(_code){
this.code = _code;
console.log(`${_className} initialised with code: ${_code}!`);
}
};
return dynamicConstructor[_className];
}
const MyClass = classFactory("Foo");
let fooInstance2 = new MyClass(123);
console.debug(fooInstance2);