在 Javascript ECMAScript 6 中从 class 名称创建对象
Create object from class name in JavasScript ECMAScript 6
我想使用 ES6 创建对象工厂,但旧式语法不适用于新语法。
我有下一个代码:
export class Column {}
export class Sequence {}
export class Checkbox {}
export class ColumnFactory {
constructor() {
this.specColumn = {
__default: 'Column',
__sequence: 'Sequence',
__checkbox: 'Checkbox'
};
}
create(name) {
let className = this.specColumn[name] ? this.specColumn[name] : this.specColumn['__default'];
return new window[className](name); // this line throw error
}
}
let factory = new ColumnFactory();
let column = factory.create('userName');
我做错了什么?
问题是 类 不是 window 对象的属性。您可以将具有属性 "pointing" 的对象改为 类:
class Column {}
class Sequence {}
class Checkbox {}
let classes = {
Column,
Sequence,
Checkbox
}
class ColumnFactory {
constructor() {
this.specColumn = {
__default: 'Column',
__sequence: 'Sequence',
__checkbox: 'Checkbox'
};
}
create(name) {
let className = this.specColumn[name] ? this.specColumn[name] : this.specColumn['__default'];
return new classes[className](name); // this line no longer throw error
}
}
let factory = new ColumnFactory();
let column = factory.create('userName');
export {ColumnFactory, Column, Sequence, Checkbox};
不要在该对象上放置 class 名称。将 classes 本身放在那里,这样您就不必依赖它们是全局的并且可以通过 window
.
访问(在浏览器中)
顺便说一句,没有充分的理由让这个工厂成为 class,您可能只会实例化它一次(单例)。把它变成一个对象:
export class Column {}
export class Sequence {}
export class Checkbox {}
export const columnFactory = {
specColumn: {
__default: Column, // <--
__sequence: Sequence, // <--
__checkbox: Checkbox // <--
},
create(name, ...args) {
let cls = this.specColumn[name] || this.specColumn.__default;
return new cls(...args);
}
};
对于那些不使用 ES6 并且想知道如何使用字符串创建 classes 的人,这里是我为使它起作用所做的工作。
"use strict";
class Person {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
window.classes = {};
window.classes.Person = Person;
document.body.innerText = JSON.stringify(new window.classes["Person"](1, 2));
如您所见,最简单的方法是将 class 添加到对象。
这是 fiddle:
https://jsfiddle.net/zxg7dsng/1/
下面是一个使用这种方法的示例项目:
https://github.com/pdxjohnny/dist-rts-client-web
有一个小而肮脏的方法可以做到这一点:
function createClassByName(name,...a) {
var c = eval(name);
return new c(...a);
}
您现在可以像这样创建 class:
let c = createClassByName( 'Person', x, y );
我更喜欢这种方法:
allThemClasses.js
export class A {}
export class B {}
export class C {}
script.js
import * as Classes from './allThemClasses';
const a = new Classes['A'];
const b = new Classes['B'];
const c = new Classes['C'];
我知道这是一个旧的 post,但最近我遇到了关于如何动态实例化 class 的相同问题
我正在使用 webpack so following the documentation 有一种方法可以使用 import() 函数动态加载模块
js/classes/MyClass.js
class MyClass {
test = null;
constructor(param) {
console.log(param)
this.test = param;
}
}
js/app.js
var p = "example";
var className = "MyClass";
import('./classes/'+className).then(function(mod) {
let myClass = new mod[className](p);
console.log(myClass);
}, function(failMsg) {
console.error("Fail to load class"+className);
console.error(failMsg);
});
注意:这个方法是异步的,我无法真正说出它的性能成本,
但它在我的简单程序上完美运行(值得一试^^)
Ps: 顺便说一下,我是 Es6 的新手(几天)我更C++ / PHP / Java 开发人员。
我希望这能帮助遇到这个问题的任何人,这不是一个坏习惯 ^^"。
澄清
还有类似的问题,包括这个已经关闭的,正在寻找JavaScript中的代理classes或者工厂函数;也称为动态 classes。这个答案是一个现代解决方案,以防您在寻找任何这些东西时找到这个答案。
答案/解决方案
截至 2022 年,我认为在浏览器中使用更优雅的解决方案。我做了一个class叫Classes
,self-registers 属性 Class
(大写C)在window上;示例下方的代码。
现在您可以让 class 那些您希望能够在全局范围内动态注册自己的引用:
// Make a class:
class Handler {
handleIt() {
// Handling it...
}
}
// Have it register itself globally:
Class.add(Handler);
// OR if you want to be a little more clear:
window.Class.add(Handler);
稍后在您的代码中,您只需要 class 的名称即可获得其原始参考:
// Get class
const handler = Class.get('Handler');
// Instantiate class for use
const muscleMan = new (handler)();
或者,甚至更简单,直接实例化它:
// Directly instantiate class for use
const muscleMan = Class.new('Handler', ...args);
代码
你可以看到latest code on my gist。在所有其他脚本之前添加此脚本,您的所有 classes 都将能够使用它注册。
/**
* Adds a global constant class that ES6 classes can register themselves with.
* This is useful for referencing dynamically named classes and instances
* where you may need to instantiate different extended classes.
*
* NOTE: This script should be called as soon as possible, preferably before all
* other scripts on a page.
*
* @class Classes
*/
class Classes {
#classes = {};
constructor() {
/**
* JavaScript Class' natively return themselves, we can take advantage
* of this to prevent duplicate setup calls from overwriting the global
* reference to this class.
*
* We need to do this since we are explicitly trying to keep a global
* reference on window. If we did not do this a developer could accidentally
* assign to window.Class again overwriting any classes previously registered.
*/
if (window.Class) {
// eslint-disable-next-line no-constructor-return
return window.Class;
}
// eslint-disable-next-line no-constructor-return
return this;
}
/**
* Add a class to the global constant.
*
* @method
* @param {Class} ref The class to add.
* @return {boolean} True if ths class was successfully registered.
*/
add(ref) {
if (typeof ref !== 'function') {
return false;
}
this.#classes[ref.prototype.constructor.name] = ref;
return true;
}
/**
* Checks if a class exists by name.
*
* @method
* @param {string} name The name of the class you would like to check.
* @return {boolean} True if this class exists, false otherwise.
*/
exists(name) {
if (this.#classes[name]) {
return true;
}
return false;
}
/**
* Retrieve a class by name.
*
* @method
* @param {string} name The name of the class you would like to retrieve.
* @return {Class|undefined} The class asked for or undefined if it was not found.
*/
get(name) {
return this.#classes[name];
}
/**
* Instantiate a new instance of a class by reference or name.
*
* @method
* @param {Class|name} name A reference to the class or the classes name.
* @param {...any} args Any arguments to pass to the classes constructor.
* @returns A new instance of the class otherwise an error is thrown.
* @throws {ReferenceError} If the class is not defined.
*/
new(name, ...args) {
// In case the dev passed the actual class reference.
if (typeof name === 'function') {
// eslint-disable-next-line new-cap
return new (name)(...args);
}
if (this.exists(name)) {
return new (this.#classes[name])(...args);
}
throw new ReferenceError(`${name} is not defined`);
}
/**
* An alias for the add method.
*
* @method
* @alias Classes.add
*/
register(ref) {
return this.add(ref);
}
}
/**
* Insure that Classes is available in the global scope as Class so other classes
* that wish to take advantage of Classes can rely on it being present.
*
* NOTE: This does not violate https://www.w3schools.com/js/js_reserved.asp
*/
const Class = new Classes();
window.Class = Class;
我想使用 ES6 创建对象工厂,但旧式语法不适用于新语法。
我有下一个代码:
export class Column {}
export class Sequence {}
export class Checkbox {}
export class ColumnFactory {
constructor() {
this.specColumn = {
__default: 'Column',
__sequence: 'Sequence',
__checkbox: 'Checkbox'
};
}
create(name) {
let className = this.specColumn[name] ? this.specColumn[name] : this.specColumn['__default'];
return new window[className](name); // this line throw error
}
}
let factory = new ColumnFactory();
let column = factory.create('userName');
我做错了什么?
问题是 类 不是 window 对象的属性。您可以将具有属性 "pointing" 的对象改为 类:
class Column {}
class Sequence {}
class Checkbox {}
let classes = {
Column,
Sequence,
Checkbox
}
class ColumnFactory {
constructor() {
this.specColumn = {
__default: 'Column',
__sequence: 'Sequence',
__checkbox: 'Checkbox'
};
}
create(name) {
let className = this.specColumn[name] ? this.specColumn[name] : this.specColumn['__default'];
return new classes[className](name); // this line no longer throw error
}
}
let factory = new ColumnFactory();
let column = factory.create('userName');
export {ColumnFactory, Column, Sequence, Checkbox};
不要在该对象上放置 class 名称。将 classes 本身放在那里,这样您就不必依赖它们是全局的并且可以通过 window
.
顺便说一句,没有充分的理由让这个工厂成为 class,您可能只会实例化它一次(单例)。把它变成一个对象:
export class Column {}
export class Sequence {}
export class Checkbox {}
export const columnFactory = {
specColumn: {
__default: Column, // <--
__sequence: Sequence, // <--
__checkbox: Checkbox // <--
},
create(name, ...args) {
let cls = this.specColumn[name] || this.specColumn.__default;
return new cls(...args);
}
};
对于那些不使用 ES6 并且想知道如何使用字符串创建 classes 的人,这里是我为使它起作用所做的工作。
"use strict";
class Person {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
window.classes = {};
window.classes.Person = Person;
document.body.innerText = JSON.stringify(new window.classes["Person"](1, 2));
如您所见,最简单的方法是将 class 添加到对象。
这是 fiddle: https://jsfiddle.net/zxg7dsng/1/
下面是一个使用这种方法的示例项目: https://github.com/pdxjohnny/dist-rts-client-web
有一个小而肮脏的方法可以做到这一点:
function createClassByName(name,...a) {
var c = eval(name);
return new c(...a);
}
您现在可以像这样创建 class:
let c = createClassByName( 'Person', x, y );
我更喜欢这种方法:
allThemClasses.js
export class A {}
export class B {}
export class C {}
script.js
import * as Classes from './allThemClasses';
const a = new Classes['A'];
const b = new Classes['B'];
const c = new Classes['C'];
我知道这是一个旧的 post,但最近我遇到了关于如何动态实例化 class 的相同问题
我正在使用 webpack so following the documentation 有一种方法可以使用 import() 函数动态加载模块
js/classes/MyClass.js
class MyClass {
test = null;
constructor(param) {
console.log(param)
this.test = param;
}
}
js/app.js
var p = "example";
var className = "MyClass";
import('./classes/'+className).then(function(mod) {
let myClass = new mod[className](p);
console.log(myClass);
}, function(failMsg) {
console.error("Fail to load class"+className);
console.error(failMsg);
});
注意:这个方法是异步的,我无法真正说出它的性能成本, 但它在我的简单程序上完美运行(值得一试^^)
Ps: 顺便说一下,我是 Es6 的新手(几天)我更C++ / PHP / Java 开发人员。
我希望这能帮助遇到这个问题的任何人,这不是一个坏习惯 ^^"。
澄清
还有类似的问题,包括这个已经关闭的
答案/解决方案
截至 2022 年,我认为在浏览器中使用更优雅的解决方案。我做了一个class叫Classes
,self-registers 属性 Class
(大写C)在window上;示例下方的代码。
现在您可以让 class 那些您希望能够在全局范围内动态注册自己的引用:
// Make a class:
class Handler {
handleIt() {
// Handling it...
}
}
// Have it register itself globally:
Class.add(Handler);
// OR if you want to be a little more clear:
window.Class.add(Handler);
稍后在您的代码中,您只需要 class 的名称即可获得其原始参考:
// Get class
const handler = Class.get('Handler');
// Instantiate class for use
const muscleMan = new (handler)();
或者,甚至更简单,直接实例化它:
// Directly instantiate class for use
const muscleMan = Class.new('Handler', ...args);
代码
你可以看到latest code on my gist。在所有其他脚本之前添加此脚本,您的所有 classes 都将能够使用它注册。
/**
* Adds a global constant class that ES6 classes can register themselves with.
* This is useful for referencing dynamically named classes and instances
* where you may need to instantiate different extended classes.
*
* NOTE: This script should be called as soon as possible, preferably before all
* other scripts on a page.
*
* @class Classes
*/
class Classes {
#classes = {};
constructor() {
/**
* JavaScript Class' natively return themselves, we can take advantage
* of this to prevent duplicate setup calls from overwriting the global
* reference to this class.
*
* We need to do this since we are explicitly trying to keep a global
* reference on window. If we did not do this a developer could accidentally
* assign to window.Class again overwriting any classes previously registered.
*/
if (window.Class) {
// eslint-disable-next-line no-constructor-return
return window.Class;
}
// eslint-disable-next-line no-constructor-return
return this;
}
/**
* Add a class to the global constant.
*
* @method
* @param {Class} ref The class to add.
* @return {boolean} True if ths class was successfully registered.
*/
add(ref) {
if (typeof ref !== 'function') {
return false;
}
this.#classes[ref.prototype.constructor.name] = ref;
return true;
}
/**
* Checks if a class exists by name.
*
* @method
* @param {string} name The name of the class you would like to check.
* @return {boolean} True if this class exists, false otherwise.
*/
exists(name) {
if (this.#classes[name]) {
return true;
}
return false;
}
/**
* Retrieve a class by name.
*
* @method
* @param {string} name The name of the class you would like to retrieve.
* @return {Class|undefined} The class asked for or undefined if it was not found.
*/
get(name) {
return this.#classes[name];
}
/**
* Instantiate a new instance of a class by reference or name.
*
* @method
* @param {Class|name} name A reference to the class or the classes name.
* @param {...any} args Any arguments to pass to the classes constructor.
* @returns A new instance of the class otherwise an error is thrown.
* @throws {ReferenceError} If the class is not defined.
*/
new(name, ...args) {
// In case the dev passed the actual class reference.
if (typeof name === 'function') {
// eslint-disable-next-line new-cap
return new (name)(...args);
}
if (this.exists(name)) {
return new (this.#classes[name])(...args);
}
throw new ReferenceError(`${name} is not defined`);
}
/**
* An alias for the add method.
*
* @method
* @alias Classes.add
*/
register(ref) {
return this.add(ref);
}
}
/**
* Insure that Classes is available in the global scope as Class so other classes
* that wish to take advantage of Classes can rely on it being present.
*
* NOTE: This does not violate https://www.w3schools.com/js/js_reserved.asp
*/
const Class = new Classes();
window.Class = Class;