封装对象工厂


\Factory of wrapped objects

另外,可以使用工厂模式来避免使用 JavaScript new 运算符显式创建对象实例:

\Alternatively, it is possible to use a factory pattern to avoid explicitly creating object instances using the JavaScript new operator:

const obj = addon.createObject();
// instead of:
// const obj = new addon.Object(); 

首先,createObject() 方法在 addon.cc 中实现:

\First, the createObject() method is implemented in addon.cc:

// addon.cc
#include <node.h>
#include "myobject.h"
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
void CreateObject(const FunctionCallbackInfo<Value>& args) {
 MyObject::NewInstance(args);
}
void InitAll(Local<Object> exports, Local<Object> module) {
 MyObject::Init(exports->GetIsolate());
 NODE_SET_METHOD(module, "exports", CreateObject);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
} // namespace demo 

myobject.h 中,添加了静态方法 NewInstance() 来处理对象的实例化。这个方法代替了 JavaScript 中的 new :

\In myobject.h, the static method NewInstance() is added to handle instantiating the object. This method takes the place of using new in JavaScript:

// 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);
 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);
 static v8::Global<v8::Function> constructor;
 double value_;
};
} // namespace demo
#endif 

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

\The implementation in myobject.cc is similar to the previous example:

// 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::Number;
using v8::Object;
using v8::String;
using v8::Value;
// Warning! This is not thread-safe, this addon cannot be used for worker
// threads.
Global<Function> MyObject::constructor;
MyObject::MyObject(double value) : value_(value) {
}
MyObject::~MyObject() {
}
void MyObject::Init(Isolate* isolate) {
 // Prepare constructor template
 Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
 tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
 tpl->InstanceTemplate()->SetInternalFieldCount(1);
 // Prototype
 NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
 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()) {
 // Invoked as constructor: `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 {
 // Invoked as plain function `MyObject(...)`, turn into construct call.
 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);
}
void MyObject::PlusOne(const FunctionCallbackInfo<Value>& args) {
 Isolate* isolate = args.GetIsolate();
 MyObject* obj = ObjectWrap::Unwrap<MyObject>(args.This());
 obj->value_ += 1;
 args.GetReturnValue().Set(Number::New(isolate, obj->value_));
}
} // namespace demo 

再一次,要构建此示例,必须将 myobject.cc 文件添加到 binding.gyp:

\Once again, to build this example, the myobject.cc file must be added to the binding.gyp:

{
 "targets": [
 {
 "target_name": "addon",
 "sources": [
 "addon.cc",
 "myobject.cc"
 ]
 }
 ]
} 

测试它:

\Test it with:

// test.js
const createObject = require('./build/Release/addon');
const obj = createObject(10);
console.log(obj.plusOne());
// Prints: 11
console.log(obj.plusOne());
// Prints: 12
console.log(obj.plusOne());
// Prints: 13
const obj2 = createObject(20);
console.log(obj2.plusOne());
// Prints: 21
console.log(obj2.plusOne());
// Prints: 22
console.log(obj2.plusOne());
// Prints: 23 

AltStyle によって変換されたページ (->オリジナル) /