也可以包装 C++ 对象/类使其可以使用 JavaScript 的 new 操作来创建新的实例:

    1. // addon.cc
    2. #include <node.h>
    3. #include "myobject.h"
    4. namespace demo {
    5. using v8::Local;
    6. using v8::Object;
    7. void InitAll(Local<Object> exports) {
    8. MyObject::Init(exports);
    9. }
    10. NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
    11. } // namespace demo

    然后,在 myobject.h 中,包装类继承自 node::ObjectWrap

    1. // myobject.h
    2. #ifndef MYOBJECT_H
    3. #define MYOBJECT_H
    4. #include <node.h>
    5. #include <node_object_wrap.h>
    6. namespace demo {
    7. class MyObject : public node::ObjectWrap {
    8. public:
    9. static void Init(v8::Local<v8::Object> exports);
    10. private:
    11. explicit MyObject(double value = 0);
    12. ~MyObject();
    13. static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
    14. static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
    15. double value_;
    16. };
    17. } // namespace demo
    18. #endif

    myobject.cc 中,实现要被开放的各种方法。 下面,通过把 plusOne() 添加到构造函数的原型来开放它:

    1. // myobject.cc
    2. #include "myobject.h"
    3. namespace demo {
    4. using v8::Context;
    5. using v8::Function;
    6. using v8::FunctionCallbackInfo;
    7. using v8::FunctionTemplate;
    8. using v8::Isolate;
    9. using v8::Local;
    10. using v8::Number;
    11. using v8::Object;
    12. using v8::ObjectTemplate;
    13. using v8::String;
    14. using v8::Value;
    15. MyObject::MyObject(double value) : value_(value) {
    16. }
    17. MyObject::~MyObject() {
    18. }
    19. void MyObject::Init(Local<Object> exports) {
    20. Isolate* isolate = exports->GetIsolate();
    21. Local<Context> context = isolate->GetCurrentContext();
    22. Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);
    23. addon_data_tpl->SetInternalFieldCount(1); // MyObject::New() 的一个字段。
    24. Local<Object> addon_data =
    25. addon_data_tpl->NewInstance(context).ToLocalChecked();
    26. // 准备构造函数模版
    27. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);
    28. tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
    29. tpl->InstanceTemplate()->SetInternalFieldCount(1);
    30. // 原型
    31. NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
    32. Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
    33. addon_data->SetInternalField(0, constructor);
    34. exports->Set(context, String::NewFromUtf8(
    35. isolate, "MyObject").ToLocalChecked(),
    36. constructor).FromJust();
    37. }
    38. void MyObject::New(const FunctionCallbackInfo<Value>& args) {
    39. Isolate* isolate = args.GetIsolate();
    40. Local<Context> context = isolate->GetCurrentContext();
    41. if (args.IsConstructCall()) {
    42. // 像构造函数一样调用:`new MyObject(...)`
    43. double value = args[0]->IsUndefined() ?
    44. 0 : args[0]->NumberValue(context).FromMaybe(0);
    45. MyObject* obj = new MyObject(value);
    46. obj->Wrap(args.This());
    47. args.GetReturnValue().Set(args.This());
    48. } else {
    49. // 像普通方法 `MyObject(...)` 一样调用,转为构造调用。
    50. const int argc = 1;
    51. Local<Value> argv[argc] = { args[0] };
    52. Local<Function> cons =
    53. args.Data().As<Object>()->GetInternalField(0).As<Function>();
    54. Local<Object> result =
    55. cons->NewInstance(context, argc, argv).ToLocalChecked();
    56. args.GetReturnValue().Set(result);
    57. }
    58. }
    59. void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
    60. Isolate* isolate = args.GetIsolate();
    61. MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.Holder());
    62. obj->value_ += 1;
    63. args.GetReturnValue().Set(Number::New(isolate, obj->value_));
    64. }
    65. } // namespace demo

    要构建这个例子,myobject.cc 文件必须被添加到 binding.gyp

    1. {
    2. "targets": [
    3. {
    4. "target_name": "addon",
    5. "sources": [
    6. "addon.cc",
    7. "myobject.cc"
    8. ]
    9. }
    10. ]
    11. }

    测试:

    1. // test.js
    2. const addon = require('./build/Release/addon');
    3. const obj = new addon.MyObject(10);
    4. console.log(obj.plusOne());
    5. // 打印: 11
    6. console.log(obj.plusOne());
    7. // 打印: 12
    8. console.log(obj.plusOne());
    9. // 打印: 13

    当对象被垃圾收集时,包装器对象的析构函数将会运行。 对于析构函数测试,有一些命令行标志可用于强制垃圾收集。 这些标志由底层 V8 JavaScript 引擎提供。 它们随时可能更改或删除。 它们没有由 Node.js 或 V8 记录,并且它们永远不应该在测试之外使用。