也可以包装 C++ 对象/类使其可以使用 JavaScript 的 new 操作来创建新的实例:
// addon.cc#include <node.h>#include "myobject.h"namespace demo {using v8::Local;using v8::Object;void InitAll(Local<Object> exports) {MyObject::Init(exports);}NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)} // namespace demo
然后,在 myobject.h 中,包装类继承自 node::ObjectWrap:
// myobject.h#ifndef MYOBJECT_H#define MYOBJECT_H#include <node.h>#include <node_object_wrap.h>namespace demo {class MyObject : public node::ObjectWrap {public:static void Init(v8::Local<v8::Object> exports);private:explicit MyObject(double value = 0);~MyObject();static void New(const v8::FunctionCallbackInfo<v8::Value>& args);static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);double value_;};} // namespace demo#endif
在 myobject.cc 中,实现要被开放的各种方法。
下面,通过把 plusOne() 添加到构造函数的原型来开放它:
// myobject.cc#include "myobject.h"namespace demo {using v8::Context;using v8::Function;using v8::FunctionCallbackInfo;using v8::FunctionTemplate;using v8::Isolate;using v8::Local;using v8::Number;using v8::Object;using v8::ObjectTemplate;using v8::String;using v8::Value;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init(Local<Object> exports) {Isolate* isolate = exports->GetIsolate();Local<Context> context = isolate->GetCurrentContext();Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);addon_data_tpl->SetInternalFieldCount(1); // MyObject::New() 的一个字段。Local<Object> addon_data =addon_data_tpl->NewInstance(context).ToLocalChecked();// 准备构造函数模版Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());tpl->InstanceTemplate()->SetInternalFieldCount(1);// 原型NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();addon_data->SetInternalField(0, constructor);exports->Set(context, String::NewFromUtf8(isolate, "MyObject").ToLocalChecked(),constructor).FromJust();}void MyObject::New(const FunctionCallbackInfo<Value>& args) {Isolate* isolate = args.GetIsolate();Local<Context> context = isolate->GetCurrentContext();if (args.IsConstructCall()) {// 像构造函数一样调用:`new MyObject(...)`double value = args[0]->IsUndefined() ?0 : args[0]->NumberValue(context).FromMaybe(0);MyObject* obj = new MyObject(value);obj->Wrap(args.This());args.GetReturnValue().Set(args.This());} else {// 像普通方法 `MyObject(...)` 一样调用,转为构造调用。const int argc = 1;Local<Value> argv[argc] = { args[0] };Local<Function> cons =args.Data().As<Object>()->GetInternalField(0).As<Function>();Local<Object> result =cons->NewInstance(context, argc, argv).ToLocalChecked();args.GetReturnValue().Set(result);}}void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {Isolate* isolate = args.GetIsolate();MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());obj->value_ += 1;args.GetReturnValue().Set(Number::New(isolate, obj->value_));}} // namespace demo
要构建这个例子,myobject.cc 文件必须被添加到 binding.gyp:
{"targets": [{"target_name": "addon","sources": ["addon.cc","myobject.cc"]}]}
测试:
// test.jsconst addon = require('./build/Release/addon');const obj = new addon.MyObject(10);console.log(obj.plusOne());// 打印: 11console.log(obj.plusOne());// 打印: 12console.log(obj.plusOne());// 打印: 13
当对象被垃圾收集时,包装器对象的析构函数将会运行。 对于析构函数测试,有一些命令行标志可用于强制垃圾收集。 这些标志由底层 V8 JavaScript 引擎提供。 它们随时可能更改或删除。 它们没有由 Node.js 或 V8 记录,并且它们永远不应该在测试之外使用。
