除了包装和返回 C++ 对象,也可以通过使用 Node.js 的辅助函数 node::ObjectWrap::Unwrap 进行去包装来传递包装的对象。
以下例子展示了一个 add() 函数,它可以把两个 MyObject 对象作为输入参数:
// addon.cc#include <node.h>#include <node_object_wrap.h>#include "myobject.h"namespace demo {using v8::Context;using v8::FunctionCallbackInfo;using v8::Isolate;using v8::Local;using v8::Number;using v8::Object;using v8::String;using v8::Value;void CreateObject(const FunctionCallbackInfo<Value>& args) {MyObject::NewInstance(args);}void Add(const FunctionCallbackInfo<Value>& args) {Isolate* isolate = args.GetIsolate();Local<Context> context = isolate->GetCurrentContext();MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(args[0]->ToObject(context).ToLocalChecked());MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(args[1]->ToObject(context).ToLocalChecked());double sum = obj1->value() + obj2->value();args.GetReturnValue().Set(Number::New(isolate, sum));}void InitAll(Local<Object> exports) {MyObject::Init(exports->GetIsolate());NODE_SET_METHOD(exports, "createObject", CreateObject);NODE_SET_METHOD(exports, "add", Add);}NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)} // namespace demo
在 myobject.h 中,新增了一个新的公共方法用于在去包装对象后访问私有值。
// 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::Isolate* isolate);static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);inline double value() const { return value_; }private:explicit MyObject(double value = 0);~MyObject();static void New(const v8::FunctionCallbackInfo<v8::Value>& args);static v8::Global<v8::Function> constructor;double value_;};} // namespace demo#endif
myobject.cc 中的实现类似之前的例子:
// myobject.cc#include <node.h>#include "myobject.h"namespace demo {using node::AddEnvironmentCleanupHook;using v8::Context;using v8::Function;using v8::FunctionCallbackInfo;using v8::FunctionTemplate;using v8::Global;using v8::Isolate;using v8::Local;using v8::Object;using v8::String;using v8::Value;// 注意!这不是线程安全的,此插件不能用于工作线程。Global<Function> MyObject::constructor;MyObject::MyObject(double value) : value_(value) {}MyObject::~MyObject() {}void MyObject::Init(Isolate* isolate) {// Prepare constructor templateLocal<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());tpl->InstanceTemplate()->SetInternalFieldCount(1);Local<Context> context = isolate->GetCurrentContext();constructor.Reset(isolate, tpl->GetFunction(context).ToLocalChecked());AddEnvironmentCleanupHook(isolate, [](void*) {constructor.Reset();}, nullptr);}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 = Local<Function>::New(isolate, constructor);Local<Object> instance =cons->NewInstance(context, argc, argv).ToLocalChecked();args.GetReturnValue().Set(instance);}}void MyObject::NewInstance(const FunctionCallbackInfo<Value>& args) {Isolate* isolate = args.GetIsolate();const unsigned argc = 1;Local<Value> argv[argc] = { args[0] };Local<Function> cons = Local<Function>::New(isolate, constructor);Local<Context> context = isolate->GetCurrentContext();Local<Object> instance =cons->NewInstance(context, argc, argv).ToLocalChecked();args.GetReturnValue().Set(instance);}} // namespace demo
测试:
// test.jsconst addon = require('./build/Release/addon');const obj1 = addon.createObject(10);const obj2 = addon.createObject(20);const result = addon.add(obj1, obj2);console.log(result);// 打印: 30
