除了包装和返回 C++ 对象,也可以通过使用 Node.js 的辅助函数 node::ObjectWrap::Unwrap 进行去包装来传递包装的对象。 以下例子展示了一个 add() 函数,它可以把两个 MyObject 对象作为输入参数:

    1. // addon.cc
    2. #include <node.h>
    3. #include <node_object_wrap.h>
    4. #include "myobject.h"
    5. namespace demo {
    6. using v8::Context;
    7. using v8::FunctionCallbackInfo;
    8. using v8::Isolate;
    9. using v8::Local;
    10. using v8::Number;
    11. using v8::Object;
    12. using v8::String;
    13. using v8::Value;
    14. void CreateObject(const FunctionCallbackInfo<Value>& args) {
    15. MyObject::NewInstance(args);
    16. }
    17. void Add(const FunctionCallbackInfo<Value>& args) {
    18. Isolate* isolate = args.GetIsolate();
    19. Local<Context> context = isolate->GetCurrentContext();
    20. MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
    21. args[0]->ToObject(context).ToLocalChecked());
    22. MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
    23. args[1]->ToObject(context).ToLocalChecked());
    24. double sum = obj1->value() + obj2->value();
    25. args.GetReturnValue().Set(Number::New(isolate, sum));
    26. }
    27. void InitAll(Local<Object> exports) {
    28. MyObject::Init(exports->GetIsolate());
    29. NODE_SET_METHOD(exports, "createObject", CreateObject);
    30. NODE_SET_METHOD(exports, "add", Add);
    31. }
    32. NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
    33. } // namespace demo

    myobject.h 中,新增了一个新的公共方法用于在去包装对象后访问私有值。

    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::Isolate* isolate);
    10. static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
    11. inline double value() const { return value_; }
    12. private:
    13. explicit MyObject(double value = 0);
    14. ~MyObject();
    15. static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
    16. static v8::Global<v8::Function> constructor;
    17. double value_;
    18. };
    19. } // namespace demo
    20. #endif

    myobject.cc 中的实现类似之前的例子:

    1. // myobject.cc
    2. #include <node.h>
    3. #include "myobject.h"
    4. namespace demo {
    5. using node::AddEnvironmentCleanupHook;
    6. using v8::Context;
    7. using v8::Function;
    8. using v8::FunctionCallbackInfo;
    9. using v8::FunctionTemplate;
    10. using v8::Global;
    11. using v8::Isolate;
    12. using v8::Local;
    13. using v8::Object;
    14. using v8::String;
    15. using v8::Value;
    16. // 注意!这不是线程安全的,此插件不能用于工作线程。
    17. Global<Function> MyObject::constructor;
    18. MyObject::MyObject(double value) : value_(value) {
    19. }
    20. MyObject::~MyObject() {
    21. }
    22. void MyObject::Init(Isolate* isolate) {
    23. // Prepare constructor template
    24. Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
    25. tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
    26. tpl->InstanceTemplate()->SetInternalFieldCount(1);
    27. Local<Context> context = isolate->GetCurrentContext();
    28. constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked());
    29. AddEnvironmentCleanupHook(isolate, [](void*) {
    30. constructor.Reset();
    31. }, nullptr);
    32. }
    33. void MyObject::New(const FunctionCallbackInfo<Value>& args) {
    34. Isolate* isolate = args.GetIsolate();
    35. Local<Context> context = isolate->GetCurrentContext();
    36. if (args.IsConstructCall()) {
    37. // 像构造函数一样调用:`new MyObject(...)`
    38. double value = args[0]->IsUndefined() ?
    39. 0 : args[0]->NumberValue(context).FromMaybe(0);
    40. MyObject* obj = new MyObject(value);
    41. obj->Wrap(args.This());
    42. args.GetReturnValue().Set(args.This());
    43. } else {
    44. // 像普通方法 `MyObject(...)` 一样调用,转为构造调用。
    45. const int argc = 1;
    46. Local<Value> argv[argc] = { args[0] };
    47. Local<Function> cons = Local<Function>::New(isolate, constructor);
    48. Local<Object> instance =
    49. cons->NewInstance(context, argc, argv).ToLocalChecked();
    50. args.GetReturnValue().Set(instance);
    51. }
    52. }
    53. void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) {
    54. Isolate* isolate = args.GetIsolate();
    55. const unsigned argc = 1;
    56. Local<Value> argv[argc] = { args[0] };
    57. Local<Function> cons = Local<Function>::New(isolate, constructor);
    58. Local<Context> context = isolate->GetCurrentContext();
    59. Local<Object> instance =
    60. cons->NewInstance(context, argc, argv).ToLocalChecked();
    61. args.GetReturnValue().Set(instance);
    62. }
    63. } // namespace demo

    测试:

    1. // test.js
    2. const addon = require('./build/Release/addon');
    3. const obj1 = addon.createObject(10);
    4. const obj2 = addon.createObject(20);
    5. const result = addon.add(obj1, obj2);
    6. console.log(result);
    7. // 打印: 30