前三篇主要学习了node-gyp的编写与编译、异步回调函数、返回值、函数参数、绑定函数名、绑定函数方法等。这对于V8 API有了一个初步的了解。第4篇将继续编写Addons V8 API 的其他用法,内容如下:

  • [x] 对象工厂
  • [x] 函数工厂
  • [x] C++对象

Object factory(对象工厂)

Addons在C++函数中能创建并返回一个新的对象。一个对象的创建与产生是通过createObject()函数完成的。

// created by http://flyfishonline.com @author orangebook
// Object factory
#include <node/node.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) {
  Isolate* isolate = args.GetIsolate();

  //创建一个对象
  Local<Object> obj = Object::New(isolate);
  //设置对象的字段为toString,并设置返回值为 对象参数
  obj->Set(String::NewFromUtf8(isolate, "toString"), args[0]->ToString());
  //返回一个对象
  args.GetReturnValue().Set(obj);
}


void Init(Local<Object> exports, Local<Object> module) {
  //绑定CreateObject到exprots对象
  NODE_SET_METHOD(module, "exports", CreateObject);
}

  //NODE_MODULE(modname, regfunc)
  //模块名为object_factory,注册Init函数
  //该object_factory一般与binding.gyp中的target_name相同,但在实际编译中,不同并不会报错
  NODE_MODULE(object_factory, Init)

}  // namespace demo

binding.gyp

{
    "targets":[
        {
            "target_name":"object_factory",
            "sources":["main.cpp"]
        }
    ]
}

javascript

var object_factory = require("./build/Release/object_factory.node");
console.log(object_factory("test").toString);

函数工厂(Function factory)

另外一个创建一个JavaScript函数的方式是使用C++函数返回给javascript

// created by http://flyfishonline.com @author orangebook
//
#include <node/node.h>

namespace function{
    using v8::Function;
    using v8::FunctionCallbackInfo;
    using v8::FunctionTemplate;
    using v8::Isolate;
    using v8::Local;
    using v8::Object;
    using v8::String;
    using v8::Value;

    //定义返回的函数
    void ToString(const FunctionCallbackInfo<Value>& args)
    {
        Isolate* isolate = args.GetIsolate();
        //设置函数的返回值为函数 
        args.GetReturnValue().Set(args[0]->ToString());
    }

    void CreateFunction(const FunctionCallbackInfo<Value> &args)
    {
        Isolate* isolate = args.GetIsolate();

        //创建一个模板方法,并绑定返回的函数
        Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate,ToString);
        //获取函数
        Local<Function> fn = tpl->GetFunction();
        //绑定函数的名称,必须个有唯性
        fn->SetName(String::NewFromUtf8(isolate,"function.ToString"));
        //设置返回值为 函数
        args.GetReturnValue().Set(fn);
    }

    void Init(Local<Object> exports,Local<Object> module)
    {
        NODE_SET_METHOD(module,"exports",CreateFunction);
    }

    NODE_MODULE(function_return,Init);

}

binding.gyp

{
    "targets":[
        {
            "target_name":"function_factory",
            "sources":["function_factory.cpp"]
        }
    ]
}

javascript

var function_factory = require("./build/Release/function_factory.node");
var fn = function_factory();//create function
console.log(fn());

C++ Wrap 对象

C++ Objects/classes允许new创建一个对象

object_bind文件中绑定C++对象,写法一般是这样的,也前几节相比,并没有太大的变化。 不同之处在于Init方法已经合并到继承C++ wrap对象的公有方法中,并且该方法是在绑定时唯一公开的。 而在javascript中创建该对象后才可以使用该对象的私有成员函数

// object_bind.cpp
// Created by flyfishonline on 16-6-22.
//

#include "CustomObject.h"

namespace quit{

    using v8::Local;
    using v8::Object;

    void InitAll(Local<Object> exports) {
        CustomObject::Init(exports);
    }

    NODE_MODULE(addon, InitAll)

}  // namespace quit

头文件中曝露出来的API,有以下几个需要注意的地址。

  • 1,引用,劳资在编写该文件时,未注意,排查了好几天,就卡在这了。对于订阅的同学不好意思了
  • 2,私有静态成员函数与公有非静态成员函数
  • 3,Persistent constructor,由v8来完成初始化构造函数?明白的同学留个言。 
// CustomObject.h
// Created by goerver on 16-6-22.
//
#ifndef NODEADDONE_LESSION4_CUSTOM_OBJECT_H
#define NODEADDONE_LESSION4_CUSTOM_OBJECT_H
#include <node/node.h>
#include <node/node_object_wrap.h>
#include <node/v8.h>
namespace quit{
    class CustomObject:public node::ObjectWrap {
    public:
        //唯一一个公开的方法,Init函数
        static void Init(v8::Local<v8::Object> exports);

    private:
        explicit CustomObject(double value = 0);
        ~CustomObject();
        //此处函数参数 `&`,引用关键字不可少
        static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
        static void PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args);
        static v8::Persistent<v8::Function> constructor;
        double value_;
    };
}
#endif //NODEADDONE_LESSION4_CUSTOM_OBJECT_H

在C++中编写对象,以及需要封装的方法。在该方法中,从之前的几个例子中可以看出,Init已经用的很多了,但是在对象中,还需要熟练编写如下几个固定写法的方法New.在此例中New成员函数,的使用分两种情况,有参数,与无参数之分。PlusOne展示了,如何在v8中获取非静态成员变量,以及返回某个值。

//CustomObject.cpp
//
// Created by goerver on 16-6-22.
// welcome http:://www.flyfishonline.com
//

#include "CustomObject.h"

namespace quit{
    using v8::Context;
    using v8::Function;
    using v8::FunctionCallbackInfo;
    using v8::FunctionTemplate;
    using v8::Isolate;
    using v8::Local;
    using v8::Number;
    using v8::Object;
    using v8::Persistent;
    using v8::String;
    using v8::Value;

    Persistent<Function> CustomObject::constructor;

    CustomObject::CustomObject(double value) : value_(value) {};
    CustomObject::~CustomObject() {}

    //该类中的`Init` 方法,将也V8绑定初始化对象
    void CustomObject::Init(Local<Object> exports) {
        Isolate *isolate = exports->GetIsolate();

        //Prepare constructor template
        //初始构造函数

        //绑定该类的创建方法为 New方法
        Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
        tpl->SetClassName(String::NewFromUtf8(isolate, "CustomObject"));
        tpl->InstanceTemplate()->SetInternalFieldCount(1);
        // 函数原型 方法添加
        NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);

        constructor.Reset(isolate, tpl->GetFunction());

        //将函数挂载到exports对象上去
        exports->Set(String::NewFromUtf8(isolate, "CustomObject"),
                     tpl->GetFunction());

    }

    void CustomObject::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
        Isolate *isolate = args.GetIsolate();

        if (args.IsConstructCall()) {
            //调用初始化构函数
            double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
            CustomObject *obj = new CustomObject(value);
            obj->Wrap(args.This());
            args.GetReturnValue().Set(args.This());
        } else {
            //调用 function 'CustomObject'的构造函数
            const int argc = 1;
            Local<Value> argv[argc] = {args[0]};
            Local<Context> context = isolate->GetCurrentContext();
            //绑定初始化构造函数
            Local<Function> cons = Local<Function>::New(isolate, constructor);
            //创建一个v8::Object对象
            Local<Object> result = cons->NewInstance(context, argc, argv).ToLocalChecked();

            args.GetReturnValue().Set(result);
        }
    }

    void CustomObject::PlusOne(const v8::FunctionCallbackInfo<v8::Value>& args) {
        Isolate *isolate = args.GetIsolate();
        //关键处,需要记住.
        //为什么不是直接写成value_++,然后设置返回值?当看到static 关键字,估计就知道了。

        CustomObject *obj = node::ObjectWrap::Unwrap<CustomObject>(args.Holder());
        obj->value_ += 1;

        args.GetReturnValue().Set(Number::New(isolate, obj->value_));
    }
}

Node-Addon模块越写越多,当项目越来越大时,应需要研究一下binding.gyp的更多用法以及对应的参数的用法需要有更多的。 劳资不想创建太多工程,就写在一起了。当然有时需要更详细的使用示例时,请参数node-gyp

{
    "targets":[
        {
            "target_name":"wrap_oop",
             "sources": [
                    "oop_demo/object_bind.cpp",
                    "oop_demo/CustomObject.cpp"
                  ]
        },
        {
            "target_name":"function_factory",
            "sources":["function_factory.cpp"]
        }
    ]
}

JS调用示例

var object = require("./build/Release/wrap_oop.node");
var obj = new object.CustomObject(10);
console.log(obj.plusOne()); // 11
console.log(obj.plusOne()); // 12
console.log(obj.plusOne()); // 13

待续Node-Addon-续5

  • [x] Factory of wrapped objects(工厂模式)
  • [x] Passing wrapped objects around(似乎只有原文更简洁些,字面意思是绕过WrapObject对象封装)
  • [x] AtExit hooks(退出钩子)

作者 @flyfishonline 2016 年 06月 30日