In contrast to QScriptEngine, where you can add custom classes if they inherit from QObject by using the Q_SCRIPT_DECLARE_QMETAOBJECT macro, the QJSEngine does not directly provide this functionality.
You can still use the Qt Meta-object system to provide interfaces for Javascript, but you have to instantiate the object in C++ and add it to the Javascript context.
Then its slots, methods defined with Q_INVOKABLE, and properties defined with Q_PROPERTY are all accessible from within the Javascript runtime.
Now you can create a factory which creates instances of your custom class CustomClass for a given QJSEngine wrapped as Javascript objects:
class CustomClassFactory : public QObject
{
Q_OBJECT
public:
CustomClassFactory(QJSEngine* engine) : m_engine(engine) {}
Q_INVOKABLE QJSValue createInstance() {
// The engine takes ownership and destroys the object if no longer required.
return m_engine->newQObject(new CustomClass());
}
private:
QJSEngine* m_engine;
}
A factory instance needs to be constructed and added to the global object of the Javascript runtime:
QJSEngine engine;
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
Now we can construct objects in Javascript with:
var obj = _customClassFactory.createInstance()
As we've come this far, lets additionally inject a constructor for the custom class into the Javascript runtime:
QJSEngine engine;
// Again, the QJSEngine will take ownership of the created object.
QJSValue factoryObj = engine.newQObject(new CustomClassFactory());
engine.globalObject().setProperty("_customClassFactory", factoryObj);
engine.evaluate(
"function CustomClass() {"
" return _customClassFactory.createInstance()"
"}");
Et voilà, now you can construct C++ object in Javascript, like you would custom Javascript classes:
var obj = new CustomClass()
For the mentioned WebSocket API you could wrap QtWebSocket for that purpose – that was exactly what I required when I came up with the proposed approach.
Note that for the sake of simplicity I omitted parameters for the constructor, but they can simply be added as well.
PS: I would have added more links to the official documentation, but due to the lack of reputation I'm not allowed to.