NAN 项目最开始就是为了抽象 nodejs 和 v8 引擎的内部实现. 基本概念就是提供了一个 npm 的安装包, 可以通过前端的包管理工具yarn
或npm
进行安装, 他包含了nan.h
的头文件, 里面对 nodejs 模块和 v8 进行了抽象. 但是 NAN 有以下缺点:
- 不完全抽象出了 V8 的 api
- 并不提供 nodejs 所有库的支持
- 不是Nodejs 官方维护的库.
所以更推荐 n-api
和 node-addon-api
实现插件;
Example
hello world
#include <nan.h>
void Method(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(Nan::New("world").ToLocalChecked());
}
void Init(v8::Local<v8::Object> exports) {
v8::Local<v8::Context> context = exports->CreationContext();
exports->Set(context,
Nan::New("hello").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Method)
->GetFunction(context)
.ToLocalChecked());
}
NODE_MODULE(hello, Init)
config building.gyp
:
{
"targets": [
{
"target_name": "hello",
"sources": [ "hello.cc" ],
"include_dirs": [
"<!(node -e \"require('nan')\")"
]
}
]
}
function arguments
#include <nan.h>
void Add(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (info.Length() < 2) {
Nan::ThrowTypeError("Wrong number of arguments");
return;
}
if (!info[0]->IsNumber() || !info[1]->IsNumber()) {
Nan::ThrowTypeError("Wrong arguments");
return;
}
double arg0 = info[0]->NumberValue(context).FromJust();
double arg1 = info[1]->NumberValue(context).FromJust();
v8::Local<v8::Number> num = Nan::New(arg0 + arg1);
info.GetReturnValue().Set(num);
}
void Init(v8::Local<v8::Object> exports) {
v8::Local<v8::Context> context = exports->CreationContext();
exports->Set(context,
Nan::New("add").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(Add)
->GetFunction(context)
.ToLocalChecked());
}
NODE_MODULE(addon, Init)
var addon = require('bindings')('addon.node')
console.log('This should be eight:', addon.add(3, 5))
callback
通过c++回调js一个函数
#include <nan.h>
void RunCallback(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Function> cb = info[0].As<v8::Function>();
const unsigned argc = 1;
v8::Local<v8::Value> argv[argc] = {Nan::New("hello world").ToLocalChecked()};
Nan::AsyncResource resource("nan:makeCallback");
resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, argc, argv);
}
void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
Nan::SetMethod(module, "exports", RunCallback);
}
NODE_MODULE(addon, Init)
Test with js:
var addon = require('bindings')('addon');
addon(function(msg){
console.log(msg); // 'hello world'
});
object_factory
通过c++函数生成js对象
#include <nan.h>
void CreateObject(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
obj->Set(context,
Nan::New("msg").ToLocalChecked(),
info[0]->ToString(context).ToLocalChecked());
info.GetReturnValue().Set(obj);
}
void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
v8::Local<v8::Context> context = exports->CreationContext();
module->Set(context,
Nan::New("exports").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(CreateObject)
->GetFunction(context)
.ToLocalChecked());
}
NODE_MODULE(addon, Init)
Test with js:
var addon = require('bindings')('addon');
var obj1 = addon('hello');
var obj2 = addon('world');
console.log(obj1.msg+' '+obj2.msg); // 'hello world'
function_factory
通过c++创建jsfunction,并由js调用此函数
#include <nan.h>
void MyFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {
info.GetReturnValue().Set(Nan::New("hello world").ToLocalChecked());
}
void CreateFunction(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
v8::Local<v8::FunctionTemplate> tpl =
Nan::New<v8::FunctionTemplate>(MyFunction);
v8::Local<v8::Function> fn = tpl->GetFunction(context).ToLocalChecked();
// omit this to make it anonymous
fn->SetName(Nan::New("theFunction").ToLocalChecked());
info.GetReturnValue().Set(fn);
}
void Init(v8::Local<v8::Object> exports, v8::Local<v8::Object> module) {
Nan::SetMethod(module, "exports", CreateFunction);
}
NODE_MODULE(addon, Init)
Test with js:
var addon = require('bindings')('addon');
var fn = addon();
console.log(fn()); // 'hello world'
object_wrap
通过c++创建js的class,并通过js调用实例方法得到结果
// addon.cc
#include <nan.h>
#include "myobject.h"
void InitAll(v8::Local<v8::Object> exports) {
MyObject::Init(exports);
}
NODE_MODULE(addon, InitAll)
// myobject.cc
#include "myobject.h"
Nan::Persistent<v8::Function> MyObject::constructor;
MyObject::MyObject(double value) : value_(value) {}
MyObject::~MyObject() {}
void MyObject::Init(v8::Local<v8::Object> exports) {
v8::Local<v8::Context> context = exports->CreationContext();
Nan::HandleScope scope;
// Prepare constructor template
v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New);
tpl->SetClassName(Nan::New("MyObject").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
Nan::SetPrototypeMethod(tpl, "value", GetValue);
Nan::SetPrototypeMethod(tpl, "plusOne", PlusOne);
Nan::SetPrototypeMethod(tpl, "multiply", Multiply);
constructor.Reset(tpl->GetFunction(context).ToLocalChecked());
exports->Set(context,
Nan::New("MyObject").ToLocalChecked(),
tpl->GetFunction(context).ToLocalChecked());
}
void MyObject::New(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
if (info.IsConstructCall()) {
// Invoked as constructor: `new MyObject(...)`
double value =
info[0]->IsUndefined() ? 0 : info[0]->NumberValue(context).FromJust();
MyObject* obj = new MyObject(value);
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
} else {
// Invoked as plain function `MyObject(...)`, turn into construct call.
const int argc = 1;
v8::Local<v8::Value> argv[argc] = {info[0]};
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
info.GetReturnValue().Set(
cons->NewInstance(context, argc, argv).ToLocalChecked());
}
}
void MyObject::GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info) {
MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());
info.GetReturnValue().Set(Nan::New(obj->value_));
}
void MyObject::PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info) {
MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());
obj->value_ += 1;
info.GetReturnValue().Set(Nan::New(obj->value_));
}
void MyObject::Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
MyObject* obj = ObjectWrap::Unwrap<MyObject>(info.Holder());
double multiple =
info[0]->IsUndefined() ? 1 : info[0]->NumberValue(context).FromJust();
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
const int argc = 1;
v8::Local<v8::Value> argv[argc] = {Nan::New(obj->value_ * multiple)};
info.GetReturnValue().Set(
cons->NewInstance(context, argc, argv).ToLocalChecked());
}
// myonject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <nan.h>
class MyObject : public Nan::ObjectWrap {
public:
static void Init(v8::Local<v8::Object> exports);
private:
explicit MyObject(double value = 0);
~MyObject();
static void New(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void GetValue(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void PlusOne(const Nan::FunctionCallbackInfo<v8::Value>& info);
static void Multiply(const Nan::FunctionCallbackInfo<v8::Value>& info);
static Nan::Persistent<v8::Function> constructor;
double value_;
};
#endif
Test with js:
var addon = require('bindings')('addon');
var obj = new addon.MyObject(10);
console.log( obj.plusOne() ); // 11
console.log( obj.plusOne() ); // 12
console.log( obj.plusOne() ); // 13
console.log( obj.multiply().value() ); // 13
console.log( obj.multiply(10).value() ); // 130
var newobj = obj.multiply(-1);
console.log( newobj.value() ); // -13
console.log( obj === newobj ); // false