правильноJavaScript
Для программистов,Node.js
Действительно наш предпочтительный язык для серверной разработки.Node.js
Преимущества производительностиGoogle
изV8引擎
, используя неблокирующийI / O模型
, полагаясь на управление событиями. Но когда дело доходит до сценариев с интенсивными вычислениями,Node.js
Не обязательно отличное выступление. К счастью, естьC++ Addonsмеханизм, который позволяет нам писать нативныеC++
модуль и можетNode.js
зовите его.
Зачем использоватьC++
модуль
-
C++
Сообщество огромно, и я хотел быNode.js
использовать определенныйC++
модуль. - Интенсивные вычислительные сценарии и высокие требования к производительности.
Например:Fabonacci
Последовательность Фибоначчи обычно решается рекурсивным способом.Здесь, чтобы отразитьNode.js
вызыватьC++
Преимущество модуля, мы не вFabonacci
механизм с использованием кэширования.
существуетNode.js
в, согласноFabonacci
Определение, мы написали следующий код,fabonacci.js
:
// fabonacci.js
function fabonacciNodeJS(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
return fabonacciNodeJS(n - 1) + fabonacciNodeJS(n - 2);
}
function TestFabonnacci(func, env, n) {
const start = (new Date()).getTime();
const result = func(n);
const end = (new Date()).getTime();
console.log(`fabonacci(${n}) run in ${env} result is ${result}, cost time is ${end - start} ms.`);
}
TestFabonnacci(fabonacciNodeJS, 'Native Node.js', 40);
Вы можете запустить эту часть программы в командной строке, и результат будет следующим:
fabonacci(40) run in Native Node.js result is 102334155, cost time is 1125 ms.
Чтобы отразить интенсивные вычислительные сценарии,Node.js
используется вC++
Преимущества модуля расширения, на которые я опираюсьC++ AddonsБыл написан следующий код,fabonacci.cc
:
// fabonacci.cc
#include <node.h>
namespace fabonacci {
using namespace v8;
static inline size_t runFabonacci(size_t n) {
if (n == 0)
{
return 0;
}
if (n == 1)
{
return 1;
}
return runFabonacci(n - 1) + runFabonacci(n - 2);
}
static void Fabonacci(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// 检查参数类型
if (!args[0]->IsNumber())
{
isolate->ThrowException(Exception::Error(String::NewFromUtf8(isolate, "argument type must be Number")));
}
size_t n = args[0]->NumberValue();
Local<Number> num = Number::New(isolate, runFabonacci(n));
args.GetReturnValue().Set(num);
}
void init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", Fabonacci);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
перед модификациейfabonacci.js
, проверить вышеC++
расширение:
// fabonacci.js
const fabonacciCPP = require('./build/Release/fabonacci');
function fabonacciNodeJS(n) {
if (n === 0) {
return 0;
}
if (n === 1) {
return 1;
}
return fabonacciNodeJS(n - 1) + fabonacciNodeJS(n - 2);
}
function TestFabonnacci(func, env, n) {
const start = (new Date()).getTime();
const result = func(n);
const end = (new Date()).getTime();
console.log(`fabonacci(${n}) run in ${env} result is ${result}, cost time is ${end - start} ms.`);
}
TestFabonnacci(fabonacciNodeJS, 'Native Node.js', 40);
TestFabonnacci(fabonacciCPP, 'C++ Addon', 40);
Запуск вышеуказанной программы дает следующие результаты:
fabonacci(40) run in Native Node.js result is 102334155, cost time is 1120 ms.
fabonacci(40) run in C++ Addon result is 102334155, cost time is 587 ms.
Видно, что вNode.js
вызыватьC++
Модуль расширения, вычисленияn = 40
Число Фибоначчи почти в два раза быстрее.
отHello World
Начинать
Теперь мы можем начать с написанияHello World
чтобы показать, как написатьC++
расширить, иNode.js
Вызывается в модуле:
Ниже приводится использованиеC++ Addonsнаписанный одинHello World
модуль, мы можемNode.js
Этот модуль вызывается из кода.
#include <node.h>
namespace helloWorld {
using namespace v8;
static void HelloWorld(const FunctionCallbackInfo<Value>& args) {
// isolate当前的V8执行环境,每个isolate执行环境相互独立
Isolate* isolate = args.GetIsolate();
// 设定返回值
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello, World!"));
}
static void init(Local<Object> exports, Local<Object> module) {
// 设定module.exports为HelloWorld函数
NODE_SET_METHOD(module, "exports", HelloWorld);
}
// 所有的 Node.js 插件必须以以下形式模式的初始化函数
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
надC++
Код эквивалентен следующемуJavaScript
Код:
module.exports.hello = () => 'world';
Сначала создайте файл с именемbinding.gyp
файл следующим образом:
{
"targets": [
{
"target_name": "fabonacci",
"sources": [ "fabonacci.cc" ]
}
]
}
binding.gyp
использовать аналогичныйJSON
формат для описания конфигурации сборки модуля. затем используйтеnode-gypнапишите намC++
Исходный код модуля скомпилирован в бинарный модуль, мы можем использоватьsudo npm install -g node-gyp
Установить глобальноnode-gyp
:
Выполнить в корневом каталоге проекта:
node-gyp configure
node-gyp build
После успешной компиляции и сборки исполняемый файлfabonacci.node
будет в корневом каталоге проекта/build/Release
каталог, мы можемNode.js
Импортируйте этот модуль:
const hello = require('./build/Release/hello');
console.log(hello()); // Hello, World!
Преобразование типов данных V8 и типов данных JavaScript
Преобразование типов данных V8 в типы данных JavaScript
согласно сДокументация v8использоватьv8::Local<v8::Value>
Заявленные данные будутV8
изGarbage Collector
управлять. Пишем следующееC++
Пример модуля, вC++
Модуль объявляет следующееV8
переменная типа и экспортируется вJavaScript
Модуль использует:
#include <node.h>
namespace datas {
using namespace v8;
static void MyFunction(const FunctionCallbackInfo<Value> &args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(isolate, "MyFunctionReturn"));
}
static void Datas(const FunctionCallbackInfo<Value> &args) {
Isolate* isolate = args.GetIsolate();
// 声明一个V8的Object类型的变量
Local<Object> object = Object::New(isolate);
// 声明一个V8的Number类型的变量
Local<Number> number = Number::New(isolate, 0);
// 声明一个V8的String类型的变量
Local<String> string = String::NewFromUtf8(isolate, "string");
// 声明一个V8的Function类型的变量
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction);
Local<Function> func = tpl->GetFunction();
// 声明一个V8的Array类型的变量
Local<Array> array = Array::New(isolate);
// 给array赋值
for (int i = 0; i < 10; ++i)
{
array->Set(i, Number::New(isolate, i));
}
// 声明一个V8的Boolean类型的变量
Local<Boolean> boolean = Boolean::New(isolate, true);
// 声明一个V8的Undefined类型的变量
Local<Value> undefined = Undefined(isolate);
// 声明一个V8的Null类型的变量
Local<Value> nu = Null(isolate);
// 设定函数的名称
func->SetName(String::NewFromUtf8(isolate, "MyFunction"));
// 给对象赋值
object->Set(String::NewFromUtf8(isolate, "number"), number);
object->Set(String::NewFromUtf8(isolate, "string"), string);
object->Set(String::NewFromUtf8(isolate, "function"), func);
object->Set(String::NewFromUtf8(isolate, "array"), array);
object->Set(String::NewFromUtf8(isolate, "boolean"), boolean);
object->Set(String::NewFromUtf8(isolate, "undefined"), undefined);
object->Set(String::NewFromUtf8(isolate, "null"), nu);
args.GetReturnValue().Set(object);
}
static void init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", Datas);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
использоватьnode-gyp
инструмент для создания вышеуказанных модулей, вNode.js
В модуле представлено:
const datas = require('./build/Release/datas');
console.log(datas());
результат операции:
Преобразование типов данных JavaScript в типы данных V8
Например, мы передаем параметрNumber
тип данныхJavaScript
переменная, вы можете использоватьv8::Number::Cast
метод будетJavaScript
Преобразовать тип данных вV8
Типы данных, мы создали следующие модулиfactory.cc
, пример фабричного шаблона для создания объекта:
#include <node.h>
namespace factory {
using namespace v8;
static void Factory(const FunctionCallbackInfo<Value> &args) {
Isolate* isolate = args.GetIsolate();
Local<Object> object = Object::New(isolate);
object->Set(String::NewFromUtf8(isolate, "name"), Local<String>::Cast(args[0])); // Cast方法实现JavaScript转换为V8数据类型
object->Set(String::NewFromUtf8(isolate, "age"), Local<Number>::Cast(args[1])); // Cast方法实现JavaScript转换为V8数据类型
args.GetReturnValue().Set(object);
}
static void init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", Factory);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, init)
}
Вызовите указанный выше модуль:
const factory = require('./build/Release/factory');
console.log(factory('counter', 21)); // { name: 'counter', age: 21 }
о других видах
Cast
позвони, посмотриДокументация V8.