addon-tools-el/include/class-wrapping.md

104 lines
3.0 KiB
Markdown

# Es5 class wrapping
This wrapping implementation diverges from standard ES6 style wrapping.
It also uses composition rather than inheritance, so it is easily pluggable.
* For **NAPI** addons, `super()` can be called from C++ side.
* Constructor is callable with `ClassName.call(obj, ...args)`.
* Multiple C++ objects can be attached to a single JS object
if it is necessary in an inheritance scenario.
* On JS side `util.inherits`
[is used](https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor),
and on C++ side there is `inheritEs5` function.
## Class Declaration
```
class ClassName {
DECLARE_ES5_CLASS(ClassName, JSClassName);
public:
static void init(Napi::Env env, Napi::Object exports);
explicit ClassName(const Napi::CallbackInfo& info);
~ClassName();
void _destroy();
private:
JS_DECLARE_GETTER(ClassName, isDestroyed);
JS_DECLARE_METHOD(ClassName, ClassName, destroy);
bool _isDestroyed;
};
```
* `DECLARE_ES5_CLASS` - adds utility declarations, the first argument
must be this class name, and the second argument will become the
name (arbitrary) of this function (constructor) in JS.
* `init` - can be used to initialize this class and export it.
* `JS_DECLARE_METHOD` - declares a method, the first argument is this class,
the second is the name of the method to be created.
* `JS_DECLARE_GETTER` - declares a getter, the first argument is this class,
the second is the name of the getter to be created.
* `JS_DECLARE_SETTER` - declares a setter, the first argument is this class,
the second is the name of the setter to be created.
## Class Implementation
```
IMPLEMENT_ES5_CLASS(ClassName);
// Fill the properties and export the class
void ClassName::init(Napi::Env env, Napi::Object exports) {
Napi::Function ctor = wrap(env);
JS_ASSIGN_METHOD(destroy);
JS_ASSIGN_GETTER(isDestroyed);
// ...
exports.Set("JSClassName", ctor);
}
ClassName::ClassName(const Napi::CallbackInfo &info) { NAPI_ENV;
super(info);
_isDestroyed = false;
// ...
}
ClassName::~ClassName() {
_destroy();
}
void ClassName::_destroy() { DES_CHECK;
// ...
_isDestroyed = true;
}
JS_IMPLEMENT_METHOD(ClassName, destroy) { THIS_CHECK;
emit("destroy");
_destroy();
RET_UNDEFINED;
}
JS_IMPLEMENT_GETTER(ClassName, isDestroyed) { THIS_CHECK;
RET_BOOL(_isDestroyed);
}
```
* `IMPLEMENT_ES5_CLASS` - implements some utility functions for class wrapping.
* `JS_ASSIGN_METHOD` - in `init()`, assigns the given method to this class.
* `JS_ASSIGN_GETTER` - in `init()`, assigns the given getter to this class.
* `JS_ASSIGN_SETTER` - in `init()`, assigns both getter and setter to this class.
It also takes only one argument because both have the same name.
* `JS_IMPLEMENT_METHOD` - implements a method, the first argument is this class,
the second is the name of the method being implemented.
* `JS_IMPLEMENT_GETTER` - implements a getter, the first argument is this class,
the second is the name of the getter being implemented.
* `JS_IMPLEMENT_SETTER` - implements a setter, the first argument is this class,
the second is the name of the setter being implemented.