Node.js插件的正确编写方式_node.js(2)
最后,我们将引入初始化方法(此方法将由V8调用并指派给constructor函数)并关闭include guard:
public: static void Init(v8::Handle exports); }; #endif
其中exports对象在JavaScript模块中的作用等同于module.exports。
stdstring.cc文件、构造函数与解析函数
现在来创建stdstring.cc文件。我们首先需要include我们的header:
#include "stdstring.h"
下面为constructor定义属性(因为它属于静态函数):
v8::Persistent STDStringWrapper::constructor;
这个为类服务的构造函数将分配s_属性:
STDStringWrapper::STDStringWrapper(std::string s) { s_ = new std::string(s); }
而解析函数将对其进行delete,从而避免内存溢出:
STDStringWrapper::~STDStringWrapper() { delete s_; }
再有,大家必须delete掉所有与new一同分配的内容,因为每一次此类情况都有可能造成异常,因此请牢牢记住上述操作或者使用共享指针。
Init方法
该方法将由V8加以调用,旨在对我们的类进行初始化(分配constructor,将我们所有打算在JavaScript当中使用的内容安置在exports对象当中):
void STDStringWrapper::Init(v8::Handle exports) {
首先,我们需要为自己的New方法创建一个函数模板:
v8::Local tpl = v8::FunctionTemplate::New(New);
这有点类似于JavaScipt当中的new Function——它允许我们准备好自己的JavaScript类。
现在我们可以根据实际需要为该函数设定名称了(如果大家漏掉了这一步,那么构造函数将处于匿名状态,即名称为function someName() {}或者function () {}):
tpl->SetClassName(v8::String::NewSymbol("STDString"));
我们利用v8::String::NewSymbol()来创建一个用于属性名称的特殊类型字符串——这能为引擎的运作节约一点点时间。
在此之后,我们需要设定我们的类实例当中包含多少个字段:
tpl->InstanceTemplate()->SetInternalFieldCount(2);
我们拥有两个方法——add()与toString(),因此我们将数量设置为2。现在我们可以将自己的方法添加到函数原型当中了:
tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("add"), v8::FunctionTemplate::New(add)->GetFunction());
tpl->PrototypeTemplate()->Set(v8::String::NewSymbol("toString"), v8::FunctionTemplate::New(toString)->GetFunction());
这部分代码量看起来比较大,但只要认真观察大家就会发现其中的规律:我们利用tpl->PrototypeTemplate()->Set()来添加每一个方法。我们还利用v8::String::NewSymbol()为它们提供名称与FunctionTemplate。
最后,我们可以将该构造函数安置于我们的constructor类属性内的exports对象中:
constructor = v8::Persistent::New(tpl->GetFunction()); exports->Set(v8::String::NewSymbol("STDString"), constructor); }
New方法
现在我们要做的是定义一个与JavaScript Object.prototype.constructor运作效果相同的方法:
v8::Handle STDStringWrapper::New(const v8::Arguments& args) {
我们首先需要为其创建一个范围:
v8::HandleScope scope;
在此之后,我们可以利用args对象的.IsConstructCall()方法来检查该构造函数是否能够利用new关键词加以调用:
if (args.IsConstructCall()) {
如果可以,我们首先如下所示将参数传递至std::string处:
v8::String::Utf8Value str(args[0]->ToString()); std::string s(*str);
……这样我们就能将它传递到我们封装类的构造函数当中了:
STDStringWrapper* obj = new STDStringWrapper(s);
在此之后,我们可以利用之前创建的该对象的.Wrap()方法(继承自node::ObjectWrap)来将它分配给this变量:
obj->Wrap(args.This());
最后,我们可以返回这个新创建的对象:
return args.This();
如果该函数无法利用new进行调用,我们也可以直接调用构造函数。接下来,我们要做的是为参数计数设置一个常数:
} else { const int argc = 1;
现在我们需要利用自己的参数创建一个数组:
v8::Local argv[argc] = { args[0] };