- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- env 环境变量
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- sqlite 轻型数据库
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
Node.js v25.3.0 文档
- Node.js v25.3.0
- 目录
-
导航
- assert 断言
- async_hooks 异步钩子
- async_hooks/context 异步上下文
- buffer 缓冲区
- C++插件
- C/C++插件(使用 Node-API)
- C++嵌入器
- child_process 子进程
- cluster 集群
- CLI 命令行
- console 控制台
- crypto 加密
- crypto/webcrypto 网络加密
- debugger 调试器
- deprecation 弃用
- dgram 数据报
- diagnostics_channel 诊断通道
- dns 域名服务器
- domain 域
- env 环境变量
- Error 错误
- events 事件触发器
- fs 文件系统
- global 全局变量
- http 超文本传输协议
- http2 超文本传输协议 2.0
- https 安全超文本传输协议
- inspector 检查器
- Intl 国际化
- module 模块
- module/cjs CommonJS 模块
- module/esm ECMAScript 模块
- module/package 包模块
- module/typescript TS 模块
- net 网络
- os 操作系统
- path 路径
- perf_hooks 性能钩子
- permission 权限
- process 进程
- punycode 域名代码
- querystring 查询字符串
- readline 逐行读取
- repl 交互式解释器
- report 诊断报告
- sea 单个可执行应用程序
- sqlite 轻型数据库
- stream 流
- stream/web 网络流
- string_decoder 字符串解码器
- test 测试
- timers 定时器
- tls 安全传输层
- trace_events 跟踪事件
- tty 终端
- url 网址
- util 实用工具
- v8 引擎
- vm 虚拟机
- wasi 网络汇编系统接口
- worker_threads 工作线程
- zlib 压缩
- 其他版本
C++ 插件#>
【C++ addons】
Addons 是用 C++ 编写的动态链接共享对象。require() 函数可以像加载普通 Node.js 模块一样加载 addons。Addons 提供了 JavaScript 与 C/C++ 库之间的接口。
【Addons are dynamically-linked shared objects written in C++. The
require() function can load addons as ordinary Node.js modules.
Addons provide an interface between JavaScript and C/C++ libraries.】
实现插件有三种选择:
【There are three options for implementing addons:】
- Node-API
nan(Node.js 的原生抽象)- 直接使用内部的 V8、libuv 和 Node.js 库
除非需要直接访问 Node-API 未暴露的功能,否则应使用 Node-API。有关 Node-API 的更多信息,请参阅 使用 Node-API 的 C/C++ 插件。
【Unless there is a need for direct access to functionality which is not
exposed by Node-API, use Node-API.
Refer to C/C++ addons with Node-API for more information on
Node-API.】
在不使用 Node-API 时,实现插件会变得更加复杂,需要了解多个组件和 API:
【When not using Node-API, implementing addons becomes more complex, requiring
knowledge of multiple components and APIs:】
- V8:Node.js 使用的 C++ 库,用于提供 JavaScript 实现。它提供了创建对象、调用函数等机制。V8 的 API 主要在
v8.h头文件中有文档说明(Node.js 源代码树中的deps/v8/include/v8.h),也可以在 在线 获取。 - libuv:一个实现 Node.js 事件循环、其工作线程以及平台上所有异步行为的 C 库。它还作为一个跨平台抽象库,提供类似 POSIX 的简便接口,使所有主要操作系统都能轻松访问许多常见的系统任务,例如与文件系统、套接字、定时器和系统事件的交互。libuv 还提供了类似 POSIX 线程的线程抽象,以支持需要超越标准事件循环的更复杂的异步插件。插件作者应避免通过 I/O 或其他耗时任务阻塞事件循环,而应通过 libuv 将工作卸载到非阻塞系统操作、工作线程或自定义的 libuv 线程中。
- Node.js 内部库:Node.js 本身导出 C++ API,插件可以使用,其中最重要的是
node::ObjectWrap类。 - 其他静态链接库(包括 OpenSSL):这些其他库位于 Node.js 源代码树的
deps/目录中。只有 libuv、OpenSSL、V8 和 zlib 的符号是由 Node.js 有意重新导出的,并且可能在各种程度上被插件使用。有关更多信息,请参见 链接到 Node.js 中包含的库。
以下所有示例均适用于 下载,并可用作插件的起点。
【All of the following examples are available for download and may be used as the starting-point for an addon.】
你好世界#>
【Hello world】
这个“Hello world”示例是一个用 C++ 编写的简单插件,相当于以下 JavaScript 代码:
【This "Hello world" example is a simple addon, written in C++, that is the equivalent of the following JavaScript code:】
module.exports.hello = () => 'world';
首先,创建文件 hello.cc:
【First, create the file hello.cc:】
// hello.cc
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;
void Method(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(
isolate, "world", NewStringType::kNormal).ToLocalChecked());
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
} // namespace demo
所有 Node.js 插件必须导出一个遵循以下模式的初始化函数:
【All Node.js addons must export an initialization function following the pattern:】
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
NODE_MODULE 后没有分号,因为它不是一个函数(参见 node.h)。
【There is no semi-colon after NODE_MODULE as it's not a function (see
node.h).】
module_name 必须与最终二进制文件的文件名匹配(不包括 .node 后缀)。
【The module_name must match the filename of the final binary (excluding
the .node suffix).】
在 hello.cc 示例中,初始化函数是 Initialize,而插件模块名称是 addon。
【In the hello.cc example, then, the initialization function is Initialize
and the addon module name is addon.】
在使用 node-gyp 构建插件时,将宏 NODE_GYP_MODULE_NAME 作为 NODE_MODULE() 的第一个参数,可以确保最终二进制文件的名称会被传递给 NODE_MODULE()。
【When building addons with node-gyp, using the macro NODE_GYP_MODULE_NAME as
the first parameter of NODE_MODULE() will ensure that the name of the final
binary will be passed to NODE_MODULE().】
使用 NODE_MODULE() 定义的插件不能在多个上下文或多个线程中同时加载。
【Addons defined with NODE_MODULE() can not be loaded in multiple contexts or
multiple threads at the same time.】
上下文感知的插件#>
【Context-aware addons】
在某些环境中,Node.js 插件可能需要在多个上下文中多次加载。例如,电子 运行时在单个进程中运行多个 Node.js 实例。每个实例都会有自己独立的 require() 缓存,因此每个实例在通过 require() 加载原生插件时都需要插件能够正确运行。这意味着插件必须支持多次初始化。
【There are environments in which Node.js addons may need to be loaded multiple
times in multiple contexts. For example, the Electron runtime runs multiple
instances of Node.js in a single process. Each instance will have its own
require() cache, and thus each instance will need a native addon to behave
correctly when loaded via require(). This means that the addon
must support multiple initializations.】
可以使用宏 NODE_MODULE_INITIALIZER 构建一个上下文感知的插件,该宏会展开为 Node.js 在加载插件时期望找到的函数名。插件因此可以像下面的示例一样进行初始化:
【A context-aware addon can be constructed by using the macro
NODE_MODULE_INITIALIZER, which expands to the name of a function which Node.js
will expect to find when it loads an addon. An addon can thus be initialized as
in the following example:】
using namespace v8;
extern "C" NODE_MODULE_EXPORT void
NODE_MODULE_INITIALIZER(Local<Object> exports,
Local<Value> module,
Local<Context> context) {
/* Perform addon initialization steps here. */
}
另一种选择是使用宏 NODE_MODULE_INIT(),它也会构建一个具有上下文感知的插件。与 NODE_MODULE() 不同,后者用于围绕给定的插件初始化函数构建插件,NODE_MODULE_INIT() 用作此类初始化函数的声明,后面跟随一个函数体。
【Another option is to use the macro NODE_MODULE_INIT(), which will also
construct a context-aware addon. Unlike NODE_MODULE(), which is used to
construct an addon around a given addon initializer function,
NODE_MODULE_INIT() serves as the declaration of such an initializer to be
followed by a function body.】
在调用 `NODE_MODULE_INIT()`` 后,函数体内可以使用以下三个变量:
【The following three variables may be used inside the function body following an
invocation of NODE_MODULE_INIT():】
本地<Object> 导出,Local<Value> 模块,以及Local<Context> context
构建上下文感知的插件需要仔细管理全局静态数据,以确保稳定性和正确性。由于插件可能被多次加载,甚至可能来自不同的线程,因此存储在插件中的任何全局静态数据都必须妥善保护,并且不能包含对 JavaScript 对象的任何持久引用。原因在于,JavaScript 对象仅在一个上下文中有效,如果从错误的上下文或与创建它们的线程不同的线程访问,可能会导致崩溃。
【Building a context-aware addon requires careful management of global static data to ensure stability and correctness. Since the addon may be loaded multiple times, potentially even from different threads, any global static data stored in the addon must be properly protected, and must not contain any persistent references to JavaScript objects. The reason for this is that JavaScript objects are only valid in one context, and will likely cause a crash when accessed from the wrong context or from a different thread than the one on which they were created.】
可以通过执行以下步骤来构建上下文感知插件,从而避免使用全局静态数据:
【The context-aware addon can be structured to avoid global static data by performing the following steps:】
-
定义一个类,它将保存每个插件实例的数据,并且具有以下形式的静态成员
static void DeleteInstance(void* data) { // Cast `data` to an instance of the class and delete it. } -
Heap-allocate an instance of this class in the addon initializer. This can be accomplished using the
newkeyword. -
Call
node::AddEnvironmentCleanupHook(), passing it the above-created instance and a pointer toDeleteInstance(). This will ensure the instance is deleted when the environment is torn down. -
Store the instance of the class in a
v8::External, and -
Pass the
v8::Externalto all methods exposed to JavaScript by passing it tov8::FunctionTemplate::New()orv8::Function::New()which creates the native-backed JavaScript functions. The third parameter ofv8::FunctionTemplate::New()orv8::Function::New()accepts thev8::Externaland makes it available in the native callback using thev8::FunctionCallbackInfo::Data()method.
这将确保每个插件实例的数据能够到达可以从 JavaScript 调用的每个绑定。每个插件实例的数据还必须传递到插件可能创建的任何异步回调中。
【This will ensure that the per-addon-instance data reaches each binding that can be called from JavaScript. The per-addon-instance data must also be passed into any asynchronous callbacks the addon may create.】
以下示例说明了上下文感知插件的实现:
【The following example illustrates the implementation of a context-aware addon:】
#include <node.h>
using namespace v8;
class AddonData {
public:
explicit AddonData(Isolate* isolate):
call_count(0) {
// Ensure this per-addon-instance data is deleted at environment cleanup.
node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this);
}
// Per-addon data.
int call_count;
static void DeleteInstance(void* data) {
delete static_cast<AddonData*>(data);
}
};
static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
// Retrieve the per-addon-instance data.
AddonData* data =
reinterpret_cast<AddonData*>(info.Data().As<External>()->Value());
data->call_count++;
info.GetReturnValue().Set((double)data->call_count);
}
// Initialize this addon to be context-aware.
NODE_MODULE_INIT(/* exports, module, context */) {
Isolate* isolate = Isolate::GetCurrent();
// Create a new instance of `AddonData` for this instance of the addon and
// tie its life cycle to that of the Node.js environment.
AddonData* data = new AddonData(isolate);
// Wrap the data in a `v8::External` so we can pass it to the method we
// expose.
Local<External> external = External::New(isolate, data);
// Expose the method `Method` to JavaScript, and make sure it receives the
// per-addon-instance data we created above by passing `external` as the
// third parameter to the `FunctionTemplate` constructor.
exports->Set(context,
String::NewFromUtf8(isolate, "method").ToLocalChecked(),
FunctionTemplate::New(isolate, Method, external)
->GetFunction(context).ToLocalChecked()).FromJust();
}
工作线程支持#>
【Worker support】
为了能够从多个 Node.js 环境中加载,例如主线程和 Worker 线程,一个插件需要做到以下之一:
【In order to be loaded from multiple Node.js environments, such as a main thread and a Worker thread, an add-on needs to either:】
- 成为一个 Node-API 插件,或者
- 如上所述,使用
NODE_MODULE_INIT()声明为上下文感知
为了支持 Worker 线程,插件在此类线程退出时需要清理它们可能分配的任何资源。这可以通过使用 AddEnvironmentCleanupHook() 函数来实现:
【In order to support Worker threads, addons need to clean up any resources
they may have allocated when such a thread exits. This can be achieved through
the usage of the AddEnvironmentCleanupHook() function:】
void AddEnvironmentCleanupHook(v8::Isolate* isolate,
void (*fun)(void* arg),
void* arg);
此函数会添加一个钩子,在给定的 Node.js 实例关闭之前运行。如果有必要,可以在执行之前使用 RemoveEnvironmentCleanupHook() 移除这些钩子,该函数具有相同的签名。回调函数按先进后出顺序运行。
【This function adds a hook that will run before a given Node.js instance shuts
down. If necessary, such hooks can be removed before they are run using
RemoveEnvironmentCleanupHook(), which has the same signature. Callbacks are
run in last-in first-out order.】
如果有必要,还可以使用一对额外的 AddEnvironmentCleanupHook() 和 RemoveEnvironmentCleanupHook() 重载,其中清理钩子接受一个回调函数。它可用于关闭异步资源,例如由插件注册的任何 libuv 句柄。
【If necessary, there is an additional pair of AddEnvironmentCleanupHook()
and RemoveEnvironmentCleanupHook() overloads, where the cleanup hook takes a
callback function. This can be used for shutting down asynchronous resources,
such as any libuv handles registered by the addon.】
下面的 addon.cc 使用了 AddEnvironmentCleanupHook:
【The following addon.cc uses AddEnvironmentCleanupHook:】
// addon.cc
#include <node.h>
#include <assert.h>
#include <stdlib.h>
using node::AddEnvironmentCleanupHook;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;
// Note: In a real-world application, do not rely on static/global data.
static char cookie[] = "yum yum";
static int cleanup_cb1_called = 0;
static int cleanup_cb2_called = 0;
static void cleanup_cb1(void* arg) {
Isolate* isolate = static_cast<Isolate*>(arg);
HandleScope scope(isolate);
Local<Object> obj = Object::New(isolate);
assert(!obj.IsEmpty()); // assert VM is still alive
assert(obj->IsObject());
cleanup_cb1_called++;
}
static void cleanup_cb2(void* arg) {
assert(arg == static_cast<void*>(cookie));
cleanup_cb2_called++;
}
static void sanity_check(void*) {
assert(cleanup_cb1_called == 1);
assert(cleanup_cb2_called == 1);
}
// Initialize this addon to be context-aware.
NODE_MODULE_INIT(/* exports, module, context */) {
Isolate* isolate = Isolate::GetCurrent();
AddEnvironmentCleanupHook(isolate, sanity_check, nullptr);
AddEnvironmentCleanupHook(isolate, cleanup_cb2, cookie);
AddEnvironmentCleanupHook(isolate, cleanup_cb1, isolate);
}
通过运行在 JavaScript 中进行测试:
【Test in JavaScript by running:】
// test.js
require('./build/Release/addon');
构建#>
【Building】
一旦源代码编写完成,就必须将其编译成二进制 addon.node 文件。为此,在项目的顶层创建一个名为 binding.gyp 的文件,使用类似 JSON 的格式描述模块的构建配置。这个文件由 node-gyp 使用,这是一款专门用于编译 Node.js 插件的工具。
【Once the source code has been written, it must be compiled into the binary
addon.node file. To do so, create a file called binding.gyp in the
top-level of the project describing the build configuration of the module
using a JSON-like format. This file is used by node-gyp, a tool written
specifically to compile Node.js addons.】
{
"targets": [
{
"target_name": "addon",
"sources": [ "hello.cc" ]
}
]
}
node-gyp 工具的一个版本打包在 Node.js 中,并作为 npm 的一部分分发。此版本不会直接提供给开发者使用,仅用于支持使用 npm install 命令来编译和安装插件的功能。希望直接使用 node-gyp 的开发者可以通过命令 npm install -g node-gyp 来安装。有关更多信息,包括平台特定的要求,请参见 node-gyp 文档。
【A version of the node-gyp utility is bundled and distributed with
Node.js as part of npm. This version is not made directly available for
developers to use and is intended only to support the ability to use the
npm install command to compile and install addons. Developers who wish to
use node-gyp directly can install it using the command
npm install -g node-gyp. See the node-gyp installation instructions for
more information, including platform-specific requirements.】
一旦创建了 binding.gyp 文件,使用 node-gyp configure 来生成当前平台的适当项目构建文件。这将在 build/ 目录中生成一个 Makefile(在 Unix 平台上)或 vcxproj 文件(在 Windows 上)。
【Once the binding.gyp file has been created, use node-gyp configure to
generate the appropriate project build files for the current platform. This
will generate either a Makefile (on Unix platforms) or a vcxproj file
(on Windows) in the build/ directory.】
接下来,执行 node-gyp build 命令来生成编译后的 addon.node 文件。该文件将被放置在 build/Release/ 目录下。
【Next, invoke the node-gyp build command to generate the compiled addon.node
file. This will be put into the build/Release/ directory.】
在使用 npm install 安装 Node.js 插件时,npm 会使用其自带的 node-gyp 版本来执行同样的操作,根据用户的平台按需生成插件的编译版本。
【When using npm install to install a Node.js addon, npm uses its own bundled
version of node-gyp to perform this same set of actions, generating a
compiled version of the addon for the user's platform on demand.】
一旦构建完成,可以通过在 Node.js 中将 require() 指向已构建的 addon.node 模块来使用该二进制插件:
【Once built, the binary addon can be used from within Node.js by pointing
require() to the built addon.node module:】
// hello.js
const addon = require('./build/Release/addon');
console.log(addon.hello());
// Prints: 'world'
由于编译后的插件二进制文件的确切路径可能因编译方式不同而有所变化(例如,有时可能在 ./build/Debug/ 目录下),插件可以使用 绑定 包来加载已编译的模块。
【Because the exact path to the compiled addon binary can vary depending on how
it is compiled (i.e. sometimes it may be in ./build/Debug/), addons can use
the bindings package to load the compiled module.】
虽然 bindings 包的实现方式在定位插件模块方面更为复杂,但它本质上使用的是类似以下的 try…catch 模式:
【While the bindings package implementation is more sophisticated in how it
locates addon modules, it is essentially using a try…catch pattern similar to:】
try {
return require('./build/Release/addon.node');
} catch (err) {
return require('./build/Debug/addon.node');
}
链接到 Node.js 中包含的库#>
【Linking to libraries included with Node.js】
Node.js 使用静态链接的库,如 V8、libuv 和 OpenSSL。所有插件都需要链接到 V8,并且也可以链接到其他任何依赖。通常,这只需包含适当的 #include <...> 语句(例如 #include <v8.h>),node-gyp 就会自动定位相应的头文件。不过,有一些注意事项需要了解:
【Node.js uses statically linked libraries such as V8, libuv, and OpenSSL. All
addons are required to link to V8 and may link to any of the other dependencies
as well. Typically, this is as simple as including the appropriate
#include <...> statements (e.g. #include <v8.h>) and node-gyp will locate
the appropriate headers automatically. However, there are a few caveats to be
aware of:】
- 当
node-gyp运行时,它会检测 Node.js 的具体发布版本,并下载完整的源代码压缩包或仅下载头文件。如果下载了完整的源代码,插件将可以完全访问 Node.js 的所有依赖。然而,如果只下载了 Node.js 头文件,则只能使用 Node.js 导出的符号。 node-gyp可以使用指向本地 Node.js 源代码镜像的--nodedir标志来运行。使用此选项,插件将可以访问完整的依赖集合。
使用 require() 加载插件#>
【Loading addons using require()】
已编译的插件二进制文件的文件名扩展名是 .node(而不是 .dll 或 .so)。require() 函数被编写为查找具有 .node 文件扩展名的文件,并将其初始化为动态链接库。
【The filename extension of the compiled addon binary is .node (as opposed
to .dll or .so). The require() function is written to look for
files with the .node file extension and initialize those as dynamically-linked
libraries.】
在调用 require() 时,通常可以省略 .node 扩展名,Node.js 仍然能够找到并初始化该插件。然而,有一个注意事项是,Node.js 会先尝试定位并加载那些恰好具有相同基本名称的模块或 JavaScript 文件。例如,如果在与二进制文件 addon.node 相同的目录中有一个文件 addon.js,那么 require('addon') 会优先加载 addon.js 文件,而不是 addon.node。
【When calling require(), the .node extension can usually be
omitted and Node.js will still find and initialize the addon. One caveat,
however, is that Node.js will first attempt to locate and load modules or
JavaScript files that happen to share the same base name. For instance, if
there is a file addon.js in the same directory as the binary addon.node,
then require('addon') will give precedence to the addon.js file
and load it instead.】
Node.js 的原生抽象#>
【Native abstractions for Node.js】
本文档中展示的每个示例都直接使用 Node.js 和 V8 的 API 来实现插件。V8 的 API 会随着每个发布版本(以及每个主要的 Node.js 版本)发生巨大变化。每次变化之后,插件可能需要更新和重新编译才能继续运行。Node.js 的发布计划旨在尽量减少此类变化的频率和影响,但 Node.js 对于确保 V8 API 的稳定性几乎无能为力。
【Each of the examples illustrated in this document directly use the Node.js and V8 APIs for implementing addons. The V8 API can, and has, changed dramatically from one V8 release to the next (and one major Node.js release to the next). With each change, addons may need to be updated and recompiled in order to continue functioning. The Node.js release schedule is designed to minimize the frequency and impact of such changes but there is little that Node.js can do to ensure stability of the V8 APIs.】
Node.js 的原生抽象(或 nan)提供了一套工具,建议插件开发者使用这些工具,以保持 V8 和 Node.js 不同版本之间的兼容性。请参阅 nan 例子,了解它如何使用的示例。
【The Native Abstractions for Node.js (or nan) provide a set of tools that
addon developers are recommended to use to keep compatibility between past and
future releases of V8 and Node.js. See the nan examples for an
illustration of how it can be used.】
Node-API#>
Node-API 是用于构建原生插件的 API。它独立于底层的 JavaScript 运行时(例如 V8),并作为 Node.js 的一部分进行维护。该 API 在 Node.js 的各个版本之间将保持应用二进制接口(ABI)稳定。它旨在将插件与底层 JavaScript 引擎的变化隔离开,并允许为一个版本编译的模块在不重新编译的情况下运行在后续版本的 Node.js 上。插件的构建/打包方法与本文件中概述的方法/工具相同(如 node-gyp 等)。唯一的区别是原生代码使用的 API 集。Node-API 提供的函数将取代 V8 或 Node.js 的原生抽象 API 来使用。
【Node-API is an API for building native addons. It is independent from the underlying JavaScript runtime (e.g. V8) and is maintained as part of Node.js itself. This API will be Application Binary Interface (ABI) stable across versions of Node.js. It is intended to insulate addons from changes in the underlying JavaScript engine and allow modules compiled for one version to run on later versions of Node.js without recompilation. Addons are built/packaged with the same approach/tools outlined in this document (node-gyp, etc.). The only difference is the set of APIs that are used by the native code. Instead of using the V8 or Native Abstractions for Node.js APIs, the functions available in the Node-API are used.】
创建和维护一个能够利用 Node-API 提供的 ABI 稳定性的插件,伴随着某些 实现考虑。
【Creating and maintaining an addon that benefits from the ABI stability provided by Node-API carries with it certain implementation considerations.】
要在上述“Hello world”示例中使用 Node-API,请将 hello.cc 的内容替换为以下内容。其他说明保持不变。
【To use Node-API in the above "Hello world" example, replace the content of
hello.cc with the following. All other instructions remain the same.】
// hello.cc using Node-API
#include <node_api.h>
namespace demo {
napi_value Method(napi_env env, napi_callback_info args) {
napi_value greeting;
napi_status status;
status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting);
if (status != napi_ok) return nullptr;
return greeting;
}
napi_value init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn);
if (status != napi_ok) return nullptr;
status = napi_set_named_property(env, exports, "hello", fn);
if (status != napi_ok) return nullptr;
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, init)
} // namespace demo
可用的函数以及如何使用它们的说明,请参见 使用 Node-API 的 C/C++ 插件。
【The functions available and how to use them are documented in C/C++ addons with Node-API.】
插件示例#>
【Addon examples】
以下是一些旨在帮助开发者入门的示例插件。这些示例使用 V8 API。有关各种 V8 调用的帮助,请参阅在线 V8 参考;有关使用的几个概念(例如句柄、作用域、函数模板等)的解释,请参阅 V8 的 嵌入器指南。
【Following are some example addons intended to help developers get started. The examples use the V8 APIs. Refer to the online V8 reference for help with the various V8 calls, and V8's Embedder's Guide for an explanation of several concepts used such as handles, scopes, function templates, etc.】
每个示例都使用以下 binding.gyp 文件:
【Each of these examples using the following binding.gyp file:】
{
"targets": [
{
"target_name": "addon",
"sources": [ "addon.cc" ]
}
]
}
在有多个 .cc 文件的情况下,只需将额外的文件名添加到 sources 数组中即可:
【In cases where there is more than one .cc file, simply add the additional
filename to the sources array:】
"sources": ["addon.cc", "myexample.cc"]
一旦 binding.gyp 文件准备好,就可以使用 node-gyp 配置并构建示例插件:
【Once the binding.gyp file is ready, the example addons can be configured and
built using node-gyp:】
node-gyp configure build
函数参数#>
【Function arguments】
插件通常会暴露可以从在 Node.js 中运行的 JavaScript 访问的对象和函数。当函数从 JavaScript 中被调用时,输入参数和返回值必须在 C/C++ 代码和 JavaScript 之间进行映射。
【Addons will typically expose objects and functions that can be accessed from JavaScript running within Node.js. When functions are invoked from JavaScript, the input arguments and return value must be mapped to and from the C/C++ code.】
下面的示例演示了如何读取从 JavaScript 传递的函数参数以及如何返回结果:
【The following example illustrates how to read function arguments passed from JavaScript and how to return a result:】
// addon.cc
#include <node.h>
namespace demo {
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
// This is the implementation of the "add" method
// Input arguments are passed using the
// const FunctionCallbackInfo<Value>& args struct
void Add(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
// Check the number of arguments passed.
if (args.Length() < 2) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate,
"Wrong number of arguments").ToLocalChecked()));
return;
}
// Check the argument types
if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8(isolate,
"Wrong arguments").ToLocalChecked()));
return;
}
// Perform the operation
double value =
args[0].As<Number>()->Value() + args[1].As<Number>()->Value();
Local<Number> num = Number::New(isolate, value);
// Set the return value (using the passed in
// FunctionCallbackInfo<Value>&)
args.GetReturnValue().Set(num);
}
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, "add", Add);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
} // namespace demo
编译后,可以在 Node.js 中加载和使用示例插件:
【Once compiled, the example addon can be required and used from within Node.js:】
// test.js
const addon = require('./build/Release/addon');
console.log('This should be eight:', addon.add(3, 5));
回调#>
【Callbacks】
在插件中,将 JavaScript 函数传递给 C++ 函数并从那里执行是常见的做法。下面的示例说明了如何调用此类回调:
【It is common practice within addons to pass JavaScript functions to a C++ function and execute them from there. The following example illustrates how to invoke such callbacks:】
// addon.cc
#include <node.h>
namespace demo {
using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Null;
using v8::Object;
using v8::String;
using v8::Value;
void RunCallback(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = {
String::NewFromUtf8(isolate,
"hello world").ToLocalChecked() };
cb->Call(context, Null(isolate), argc, argv).ToLocalChecked();
}
void Init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", RunCallback);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
} // namespace demo
这个示例使用了 Init() 的两参数形式,其第二个参数接收完整的 module 对象。这样,addon 可以用一个单独的函数完全覆盖 exports,而不是将函数作为 exports 的一个属性添加。
【This example uses a two-argument form of Init() that receives the full
module object as the second argument. This allows the addon to completely
overwrite exports with a single function instead of adding the function as a
property of exports.】
要测试它,则运行以下 JavaScript:
【To test it, run the following JavaScript:】
// test.js
const addon = require('./build/Release/addon');
addon((msg) => {
console.log(msg);
// Prints: 'hello world'
});
在这个例子中,回调函数是同步调用的。
【In this example, the callback function is invoked synchronously.】
对象工厂#>
【Object factory】
插件可以在 C++ 函数中创建并返回新对象,如下面的示例所示。一个对象被创建并返回,带有一个属性 msg,该属性会回显传递给 createObject() 的字符串:
【Addons can create and return new objects from within a C++ function as
illustrated in the following example. An object is created and returned with a
property msg that echoes the string passed to createObject():】
// addon.cc
#include <node.h>
namespace demo {
using v8::Context;
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<Context> context = isolate->GetCurrentContext();
Local<Object> obj = Object::New(isolate);
obj->Set(context,
String::NewFromUtf8(isolate,
"msg").ToLocalChecked(),
args[0]->ToString(context).ToLocalChecked())
.FromJust();
args.GetReturnValue().Set(obj);
}
void Init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", CreateObject);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
} // namespace demo
在 JavaScript 中测试它:
【To test it in JavaScript:】
// test.js
const addon = require('./build/Release/addon');
const obj1 = addon('hello');
const obj2 = addon('world');
console.log(obj1.msg, obj2.msg);
// Prints: 'hello world'
函数工厂#>
【Function factory】
另一个常见的场景是创建封装 C++ 函数的 JavaScript 函数,并将它们返回给 JavaScript:
【Another common scenario is creating JavaScript functions that wrap C++ functions and returning those back to JavaScript:】
// addon.cc
#include <node.h>
namespace demo {
using v8::Context;
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 MyFunction(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
args.GetReturnValue().Set(String::NewFromUtf8(
isolate, "hello world").ToLocalChecked());
}
void CreateFunction(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, MyFunction);
Local<Function> fn = tpl->GetFunction(context).ToLocalChecked();
// omit this to make it anonymous
fn->SetName(String::NewFromUtf8(
isolate, "theFunction").ToLocalChecked());
args.GetReturnValue().Set(fn);
}
void Init(Local<Object> exports, Local<Object> module) {
NODE_SET_METHOD(module, "exports", CreateFunction);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
} // namespace demo
去测试:
【To test:】
// test.js
const addon = require('./build/Release/addon');
const fn = addon();
console.log(fn());
// Prints: 'hello world'
封装 C++ 对象#>
【Wrapping C++ objects】
也可以以一种方式封装 C++ 对象/类,使得可以使用 JavaScript 的 new 操作符创建新实例:
【It is also possible to wrap C++ objects/classes in a way that allows new
instances to be created using the JavaScript new operator:】
// addon.cc
#include <node.h>
#include "myobject.h"
namespace demo {
using v8::Local;
using v8::Object;
void InitAll(Local<Object> exports) {
MyObject::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
} // namespace demo
然后,在 myobject.h 中,封装类继承自 node::ObjectWrap :
【Then, in myobject.h, the wrapper class inherits from node::ObjectWrap:】
// 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::Local<v8::Object> exports);
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);
double value_;
};
} // namespace demo
#endif
在 myobject.cc 中,实现需要被暴露的各种方法。在下面的代码中,通过将 plusOne() 方法添加到构造函数的原型上来暴露它:
【In myobject.cc, implement the various methods that are to be exposed.
In the following code, the method plusOne() is exposed by adding it to the
constructor's prototype:】
// myobject.cc
#include "myobject.h"
namespace demo {
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::ObjectTemplate;
using v8::String;
using v8::Value;
MyObject::MyObject(double value) : value_(value) {
}
MyObject::~MyObject() {
}
void MyObject::Init(Local<Object> exports) {
Isolate* isolate = Isolate::GetCurrent();
Local<Context> context = isolate->GetCurrentContext();
Local<ObjectTemplate> addon_data_tpl = ObjectTemplate::New(isolate);
addon_data_tpl->SetInternalFieldCount(1); // 1 field for the MyObject::New()
Local<Object> addon_data =
addon_data_tpl->NewInstance(context).ToLocalChecked();
// Prepare constructor template
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New, addon_data);
tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
// Prototype
NODE_SET_PROTOTYPE_METHOD(tpl, "plusOne", PlusOne);
Local<Function> constructor = tpl->GetFunction(context).ToLocalChecked();
addon_data->SetInternalField(0, constructor);
exports->Set(context, String::NewFromUtf8(
isolate, "MyObject").ToLocalChecked(),
constructor).FromJust();
}
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 =
args.Data().As<Object>()->GetInternalField(0)
.As<Value>().As<Function>();
Local<Object> result =
cons->NewInstance(context, argc, argv).ToLocalChecked();
args.GetReturnValue().Set(result);
}
}
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 中:
【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 addon = require('./build/Release/addon');
const obj = new addon.MyObject(10);
console.log(obj.plusOne());
// Prints: 11
console.log(obj.plusOne());
// Prints: 12
console.log(obj.plusOne());
// Prints: 13
封装对象的析构函数将在对象被垃圾回收时运行。对于析构函数的测试,可以使用命令行标志来强制进行垃圾回收。这些标志由底层的 V8 JavaScript 引擎提供。它们可能随时更改或移除。Node.js 或 V8 并未对其提供文档,且绝不应在测试之外使用这些标志。
【The destructor for a wrapper object will run when the object is garbage-collected. For destructor testing, there are command-line flags that can be used to make it possible to force garbage collection. These flags are provided by the underlying V8 JavaScript engine. They are subject to change or removal at any time. They are not documented by Node.js or V8, and they should never be used outside of testing.】
在关闭进程或工作线程时,JS 引擎不会调用析构函数。因此,用户有责任跟踪这些对象并确保它们被正确销毁,以避免资源泄漏。
【During shutdown of the process or worker threads destructors are not called by the JS engine. Therefore it's the responsibility of the user to track these objects and ensure proper destruction to avoid resource leaks.】
封装对象工厂#>
【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();
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();
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 = Isolate::GetCurrent();
// 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
传递封装的对象#>
【Passing wrapped objects around】
除了封装和返回 C++ 对象之外,还可以通过使用 Node.js 辅助函数 node::ObjectWrap::Unwrap 解包它们来传递已封装的对象。下面的例子展示了一个函数 add(),它可以接受两个 MyObject 对象作为输入参数:
【In addition to wrapping and returning C++ objects, it is possible to pass
wrapped objects around by unwrapping them with the Node.js helper function
node::ObjectWrap::Unwrap. The following examples shows a function add()
that can take two MyObject objects as input arguments:】
// addon.cc
#include <node.h>
#include <node_object_wrap.h>
#include "myobject.h"
namespace demo {
using v8::Context;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;
void CreateObject(const FunctionCallbackInfo<Value>& args) {
MyObject::NewInstance(args);
}
void Add(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
MyObject* obj1 = node::ObjectWrap::Unwrap<MyObject>(
args[0]->ToObject(context).ToLocalChecked());
MyObject* obj2 = node::ObjectWrap::Unwrap<MyObject>(
args[1]->ToObject(context).ToLocalChecked());
double sum = obj1->value() + obj2->value();
args.GetReturnValue().Set(Number::New(isolate, sum));
}
void InitAll(Local<Object> exports) {
MyObject::Init();
NODE_SET_METHOD(exports, "createObject", CreateObject);
NODE_SET_METHOD(exports, "add", Add);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
} // namespace demo
在 myobject.h 中,添加了一个新的公共方法,以允许在解包对象后访问私有值。
【In myobject.h, a new public method is added to allow access to private values
after unwrapping the object.】
// 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();
static void NewInstance(const v8::FunctionCallbackInfo<v8::Value>& args);
inline double value() const { return value_; }
private:
explicit MyObject(double value = 0);
~MyObject();
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static v8::Global<v8::Function> constructor;
double value_;
};
} // namespace demo
#endif
myobject.cc 的实现与之前的版本类似:
【The implementation of myobject.cc remains similar to the previous version:】
// 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::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 = Isolate::GetCurrent();
// Prepare constructor template
Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
tpl->SetClassName(String::NewFromUtf8(isolate, "MyObject").ToLocalChecked());
tpl->InstanceTemplate()->SetInternalFieldCount(1);
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);
}
} // namespace demo
测试它:
【Test it with:】
// test.js
const addon = require('./build/Release/addon');
const obj1 = addon.createObject(10);
const obj2 = addon.createObject(20);
const result = addon.add(obj1, obj2);
console.log(result);
// Prints: 30