跳至主要內容

Node-API

樱桃茶大约 546 分钟

Node-APIopen in new window

Node-API(以前称为 N-API)是 Node.js 提供的一个 API 层,它允许你用 C 或 C++编写插件。之所以使用 Node-API,主要是因为它提供了一种与底层 JavaScript 引擎(如 V8)解耦的方式来构建原生扩展。这意味着当 Node.js 更新其内部引擎时,使用 Node-API 构建的插件不需要重写来适应新版本,从而增加了扩展的稳定性和兼容性。

在实际应用中,Node-API 可以用于多种情况,比如:

  1. 性能密集型任务:如果你的 Node.js 应用需要处理大量计算,可能会由于 JavaScript 的性能限制而变慢。通过 Node-API,你可以用 C 或 C++编写这些计算密集型的任务,然后在 Node.js 中调用,利用 C/C++的高效率来提升整体性能。

  2. 使用现有的 C/C++库:如果有一个功能强大的 C/C++库并且你希望在你的 Node.js 应用中使用它,那么你可以通过 Node-API 创建一个绑定(binding),让 JavaScript 代码能够调用这个库。

  3. 系统级操作:当需要进行底层操作系统交互(如直接与硬件通信)时,Node-API 可以使得 Node.js 拥有调用系统 API 的能力。

例如,假设我们有一个 C++函数用来计算斐波那契数列的第 N 项,因为这是一个 CPU 密集型任务,所以我们期望在速度上有所提升:

// fibonacci.cpp
##include `<`napi.h>

int Fibonacci(int n) {
  if (n `<`= 1) {
    return n;
  }
  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Napi::Number FibonacciWrapped(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() `<` 1 || !info[0].IsNumber()) {
    Napi::TypeError::New(env, "Number expected").ThrowAsJavaScriptException();
  }

  Napi::Number first = info[0].As`<`Napi::Number>();

  int result = Fibonacci(first.Int32Value());
  return Napi::Number::New(env, result);
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(
    Napi::String::New(env, "fibonacci"),
    Napi::Function::New(env, FibonacciWrapped)
  );

  return exports;
}

NODE_API_MODULE(fibonacci, Init)

这段代码首先包括了 Node-API 必需的头文件,然后定义了一个 Fibonacci 函数用来递归计算斐波那契数列。我们还定义了一个 FibonacciWrapped 函数来作为 Node-API 的接口,它将 JavaScript 传入的参数转化为 C++类型,并调用纯 C++的 Fibonacci 函数。最后,Init 函数注册了 FibonacciWrapped 函数,使其在 Node.js 模块中以 fibonacci 名称被导出。

安装完必要的 Node.js 和 C++ 构建工具后,你可以编译这个扩展然后在 Node.js 代码中这样使用它:

// index.js
const addon = require("./build/Release/fibonacci");

console.log(addon.fibonacci(10)); // 输出斐波那契数列的第10项

在这个简单的例子中,我们看到 Node-API 如何连接 Node.js 和 C++代码,允许 JavaScript 调用原生 C++函数,以此来实现更优的性能。这只是 Node-API 功能的冰山一角,但它非常清楚地展示了 Node-API 如何被用来提升 Node.js 应用的能力。

Implications of ABI stabilityopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境,允许你在服务器端运行 JavaScript 代码。在 Node.js 中,“ABI”是“应用程序二进制接口”的缩写,它定义了应用程序(这里指 Node.js 的模块或插件)与操作系统之间或不同应用程序组件之间如何相互交互的详细规范。

当我们谈论到 ABI 稳定性时,我们主要关注的是编译过的 Node.js 模块(通常使用 C 或 C++ 编写的扩展)是否可以在不同版本的 Node.js 中无需重新编译而直接使用。这意味着一个模块的二进制版本可以适配多个版本的 Node.js,这对于开发者来说非常方便,因为模块的兼容性得到了增强。

ABI 稳定性的影响

1. 模块开发者: 如果 ABI 是稳定的,则模块开发者不需要为每个新版本的 Node.js 重新编译他们的模块。这减少了维护工作量,并使得模块更容易被广泛使用。例如,如果你创建了一个用于图像处理的 Node.js 模块,并且该模块采用 C++ 编写,那么你的用户就可以在多个 Node.js 版本中使用这个模块,而不需要等待你发布特定于每个版本的预编译二进制文件。

2. 应用开发者: 应用开发者可以更有信心地升级他们的 Node.js 运行时,因为他们知道依赖的模块很可能仍然能够正常工作。在实践中,这意味着如果你的项目使用了某些依赖库,比如用于数据库连接的 node-oracledb,即使在 Node.js 更新后,通常也无需更改任何代码或等待库的更新。

3. Node.js 社区: 整个 Node.js 生态系统受益于 ABI 稳定性,因为它降低了各种版本间的兼容性问题,促进了模块共享和复用。

实际运用的例子:

  • 原生模块兼容性: 如果你安装了一个用于性能监控的原生 Node.js 模块,比如 v8-profiler,并且 Node.js 有一个新版本发布了。由于 ABI 稳定性,理想情况下,你可以直接在新版本的 Node.js 上运行你的应用程序,而不必担心 v8-profiler 模块会因为新版本而不兼容。

  • 升级 Node.js 版本: 当团队决定将项目从 Node.js v20 升级到 Node.js v21 时,他们检查了所有依赖项以确保兼容性。如果所有关键模块都支持跨版本的 ABI 稳定性,这个升级过程会变得简单快捷,因为团队不必担心这些模块需要重新编译。

  • 跨平台模块: 开发者可能创建了一个用于读取系统信息的模块,并且希望它能在 Windows、macOS 和 Linux 上运行。如果这个模块遵循 ABI 稳定性的标准,它将能够在不同的操作系统上,以及在每个操作系统上的不同 Node.js 版本中运行,而无需进行任何特别的适配工作。

总结来说,ABI 稳定性在 Node.js 中是一个重要特性,它极大地提高了模块跨版本和跨平台的兼容性,对于开发者和最终用户而言都是一个巨大的利好。

Buildingopen in new window

Node.js 是一个运行时环境,它能让你使用 JavaScript 来编写服务器端的应用。其中一个特性是 N-API,这是一个构建原生模块的 API。在 Node.js 中,原生模块通常是用 C 或者 C++ 写的插件,它们可以直接和操作系统层进行交互。

在 Node.js v21.7.1 的文档中,“Building”这一部分专门介绍了如何构建一个使用 N-API 的原生模块。以下是这一过程的简单解释和实例:

构建原生模块的步骤

  1. 配置 binding.gyp 文件: 为了构建原生模块,首先你需要创建一个叫做 binding.gyp 的文件,这个文件包含了描述模块如何被构建的配置信息。这个文件的格式基于 Python 的 GYP(Generate Your Projects)工具,即使你不熟悉 Python 也没关系,因为格式相对直观。

  2. 编写 C/C++ 代码: 接下来,你将编写实际的 C 或 C++ 代码。这些代码会使用 N-API 提供的函数来与 JavaScript 层的代码进行交互。

  3. 编译和链接: 当你有了 binding.gyp 和 C/C++ 源码文件后,你可以使用 node-gyp 命令行工具来编译你的原生模块。node-gyp 会读取 binding.gyp 文件,执行必要的编译和链接步骤生成二进制文件。

实际例子

  1. 创建 binding.gyp 文件: 假设我们正在创建一个叫做 "awesome" 的原生模块,你可能会有一个像这样的 binding.gyp 文件:

    {
      "targets": [
        {
          "target_name": "awesome",
          "sources": ["src/awesome.cc"],
          "include_dirs": [
            "`<`!@(node -p \"require('node-addon-api').include\")"
          ],
          "dependencies": ["`<`!(node -p \"require('node-addon-api').gyp\")"],
          "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"]
        }
      ]
    }
    

    这里指定了编译目标、源文件、包含目录、依赖关系及宏定义。

  2. 编写 awesome.cc 源文件: 在 C++源文件中,你会使用 N-API 定义的函数和宏。比如,你可能会写一个简单的函数来返回字符串 "hello world":

    ##include `<`napi.h>
    
    Napi::String HelloMethod(const Napi::CallbackInfo& info) {
      Napi::Env env = info.Env();
      return Napi::String::New(env, "hello world");
    }
    
    Napi::Object Init(Napi::Env env, Napi::Object exports) {
      exports.Set("hello", Napi::Function::New(env, HelloMethod));
      return exports;
    }
    
    NODE_API_MODULE(awesome, Init)
    

    这里定义了一个名为 HelloMethod 的函数,并在模块初始化时把它暴露出来。

  3. 编译模块: 最后,你需要在命令行中运行 node-gyp configurenode-gyp build。这些命令会分别配置和编译你的模块。

经过上述步骤,你就成功构建了一个原生模块。然后你可以通过 require 在你的 Node.js 代码中载入并使用这个模块。例如,在 JavaScript 中,你可以这样使用刚才构建的 "awesome" 模块:

const awesomeModule = require("./build/Release/awesome");
console.log(awesomeModule.hello()); // 输出: hello world

希望以上解释清楚了 Node.js 中构建原生模块的过程和一些基本概念。如果你想深入学习,建议阅读官方文档以及涉及 node-gyp 和 N-API 的相关资料。

Build toolsopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境,使得你可以在服务器端运行 JavaScript。这里我们聚焦于 Node.js 中的一个特定功能:N-API。

N-API 是 Node.js 提供的一组 C API,它是用来构建原生插件的。所谓的原生插件,就是使用像 C 或 C++这样的系统编程语言编写的模块,这些模块可以直接和 Node.js 二进制接口交互,无需通过 JavaScript。原生插件通常用于性能敏感的操作或者调用已有的系統级别库。

想要构建这些原生插件,你需要一些构建工具。而在 Node.js v21.7.1 的上下文中提到的 "Build tools" 很可能是指那些用来编译和链接原生模块的工具,比如 node-gyp、cmake-js 等。

node-gyp 是一个跨平台的命令行工具,它使用 Python 和 GYP(Generate Your Projects)来为 Node.js 原生插件生成必要的项目构建文件,适用于多种系统。

例如,如果你正在开发一个原生插件来加速图像处理,你会编写一些 C++ 代码来处理图像数据,然后使用 node-gyp 来编译这段代码,使其变成 Node.js 可以加载并使用的本地模块。

安装 node-gyp 的步骤大致如下:

  1. 安装 Node.js,它自带 npm(node package manager),npm 用来管理 Node.js 模块。
  2. 使用 npm 全局安装 node-gyp:npm install -g node-gyp
  3. 你需要确保你的电脑上安装了所有必要的构建工具和配置。对于 Windows, 你可能需要安装 windows-build-tools;对于 macOS 或 Linux,你需要安装 python 和 make 工具链等。

一个简单的 node-gyp 配置示例(binding.gyp 文件):

{
  "targets": [
    {
      "target_name": "myaddon",
      "sources": ["src/myaddon.cc"]
    }
  ]
}

这个配置定义了一个目标(你的插件),以及源代码的位置。当你在终端执行 node-gyp configurenode-gyp build 时,node-gyp 将会读取这个文件,并且根据当前平台生成相应的构建文件,然后编译源代码。

总结一下,对于 Node.js v21.7.1 中提及的 Build tools,这主要指的是用于构建 N-API 原生插件的工具集,它们能够帮助开发者编译和链接他们用 C/C++ 编写的代码,使之成为 Node.js 可以利用的原生扩展。最常见的这类工具就是 node-gyp。

node-gypopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者可以使用 JavaScript 来编写服务器端的软件。而 node-gyp 并不是 Node.js 核心功能的一部分,而是一个第三方的工具,用来编译 Node.js 的 C++ 扩展模块。

在 Node.js 中,大多数模块都是用 JavaScript 写的,但有些情况下,为了性能或访问系统资源等原因,需要用 C 或 C++ 来编写扩展。这些扩展就是通过 node-gyp 构建的。

node-gyp 是一个跨平台的命令行工具,它需要 Python 和一个合适的 C/C++ 编译器(例如,在 Windows 上可能是 Visual Studio 的构建工具)。node-gyp 使用 GYP(Generate Your Projects)项目生成所需的构建文件(比如 makefile 或者 Visual Studio 的 .vcxproj 文件),然后编译和链接 C++ 代码生成对应的二进制模块。

举几个实际运用的例子:

  1. 数据库绑定:比如说,Node.js 与 MySQL 的通信可以使用纯 JavaScript 写的模块,但也可以使用像 mysql2 这样的模块,后者使用 node-gyp 编译性能更高的 C++ 代码来加速数据库操作。

  2. 性能密集型计算:如果你在做图像处理或者执行复杂的数学运算,可能会选择用 C++ 来编写这部分逻辑以获得更好的性能,再通过 node-gyp 编译成 Node.js 模块提供给 JavaScript 调用。

  3. 系统级别的操作:当你需要进行低级文件操作、网络通信或直接与操作系统对话时,可能会借助 node-gyp 编译一些 C++ 模块以便直接调用系统 API。

要安装和使用 node-gyp,通常会先用 npm(Node.js 的包管理器)来安装它:

npm install -g node-gyp

然后,如果你下载了一个需要编译的 Node.js 模块,一般在模块的目录下运行以下命令来编译模块:

node-gyp configure
node-gyp build

configure 命令会根据当前平台生成构建文件,build 命令实际上会进行编译。

总的来说,node-gyp 是一个非常强大的工具,可以帮助开发者将 C/C++ 代码集成到 Node.js 应用中,这在需要特殊处理或优化性能的场景下非常有用。

CMake.jsopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它允许你使用 JavaScript 编写服务器端代码。CMake.js 则是为了简化在 Node.js 中编译和构建本地(native)模块的扩展工具。

通常情况下,当你想要在 Node.js 中使用一些需要编译的 C++代码时,你会用到一个名叫 node-gyp 的工具。node-gyp 使用 Python 和 gyp 来构建这些本地模块。然而,并不是所有的人都熟悉 Python 或者想要安装 Python 只为了构建一个项目。因此,CMake.js 就被创建出来作为 node-gyp 的替代品。

CMake.js 利用 CMake 来构建本地模块。CMake 是一个非常流行的跨平台自动化构建工具,它能够生成本地构建环境(例如 makefile 或者 Visual Studio 的项目文件)。

使用 CMake.js 的步骤大致如下:

  1. 安装 CMake.js:首先,你需要在你的项目中安装 CMake.js。你可以通过 npm 命令来安装它:

    npm install --save-dev cmake-js
    
  2. 配置 CMakeLists.txt 文件:在你的项目根目录中创建一个CMakeLists.txt文件。这个文件告诉 CMake 怎样去构建你的本地模块。例如:

    cmake_minimum_required(VERSION 3.7)
    project(MyModule)
    
    add_library(MyModule SHARED
        src/my_module.cpp
    )
    
    set_target_properties(MyModule PROPERTIES PREFIX "" SUFFIX ".node")
    
  3. 编译本地模块:通过 CMake.js 提供的命令行工具或者集成到 npm 脚本中来构建你的模块。

    cmake-js compile
    

    或者在package.json中添加一个脚本:

    {
      "scripts": {
        "build": "cmake-js compile"
      }
    }
    

    然后你可以用 npm run build 来构建你的模块。

  4. 在 Node.js 中使用你的模块:构建完成后,你就可以像其他任何 npm 模块一样在你的 JavaScript 代码中引入并使用你的本地模块了。

实际应用例子:

假设你有一段复杂的数学计算,这段计算在 C++中实现会比 JavaScript 快很多。你可以将这部分逻辑写在my_math.cpp文件中,然后用 CMake.js 来编译这个 C++代码为 Node.js 可加载的本地模块。 Node.js 代码可以直接调用这个模块,以获得更好的性能。

请记住,CMake.js 主要是面向已经有 C++背景,并且希望能有效地将这些代码与 Node.js 结合起来的开发人员。对于编程新手来说,如果没有必要处理 C++代码,开始时可能不需要深入学习 CMake.js。

Uploading precompiled binariesopen in new window

Node.js 中的 N-API 是一个用于构建本地插件的 API,它提供了一套稳定的在各个 Node 版本间通用的函数,这意味着如果你使用 N-API 编写的插件或者模块,则可以在不同版本的 Node.js 中运行而不需要重新编译。

当开发者创建了一个原生模块(也就是用 C 或者 C++书写的直接和 Node.js 交互的模块)后,他们通常会希望用户安装时能够直接下载预编译好的二进制文件,而不是从源代码开始编译。这样可以节省用户的时间,特别是对那些不熟悉本地编译环境的用户来说更加友好。

上传预编译的二进制文件通常是通过使用一些第三方服务和工具完成的,例如:

  1. node-pre-gyp: 这是一个让你能够发布和安装 Node.js C++插件预编译版本的工具。在你的项目中,你可以配置node-pre-gyp来指定哪些平台和 Node.js 版本的预编译文件应该被上传到存储服务器上(比如 Amazon S3、GitHub Releases 等)。当用户安装你的模块时,node-pre-gyp会查找匹配用户平台和 Node.js 版本的预编译二进制文件,并尝试下载它来安装。

  2. prebuild / prebuild-install: 这是另一组工具,工作方式类似于node-pre-gyp,但有其特定的配置和特性。prebuild负责创建预编译的二进制文件,并可以将它们上传到 GitHub Releases 上。而prebuild-install则被用于在用户安装你的模块时下载并安装这些预编译的二进制文件。

实际运用的例子: 假设你创建了一个名为"awesome-native-module"的 Node.js 原生模块,它提供了一些用 C++编写的功能。你想要让用户在安装你的模块时能够轻松获得预编译的二进制文件。

这里是怎么做的大致步骤:

  1. 在你的模块中加入node-pre-gyp或者prebuild作为依赖项,并且配置它们的路径,指向你想要上传二进制文件的地点。

  2. 设置 CI/CD 流程(比如 GitHub Actions),在每次你推送代码更新或者创建一个新的发布时自动运行构建过程,并且触发node-pre-gypprebuild去创建和上传新的预编译二进制文件。

  3. 当用户执行npm install awesome-native-module时,node-pre-gypprebuild-install会被调用。它们会检查用户的系统和 Node 版本,尝试去下载对应的预编译二进制文件进行安装。如果没有找到匹配的预编译文件,它们会退回到从源码编译。

通过这种方式,你可以提高用户安装你原生模块的体验,减少他们需要处理编译问题的机会。

node-pre-gypopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它让开发者能够使用 JavaScript 来编写服务端代码。Node.js 高效的事件驱动、非阻塞 I/O 模型使其非常适用于构建快速的、可扩展的网络应用。

在 Node.js 的生态系统中,有很多原生模块(native modules),即用 C 或 C++ 编写的模块,它们可以通过 Node.js 提供的 N-API 接口与 JavaScript 代码交互。这些模块通常是为了提高性能或访问一些底层系统功能,比如文件系统操作、网络通信或图形处理等。

node-pre-gyp 是一个工具和库,它允许开发者为他们的 Node.js 原生模块编译并发布预编译的二进制包。这些二进制包可以针对不同的平台(如 Windows、macOS 和 Linux)以及不同版本的 Node.js 提前编译好,并且可以在安装时直接使用,而无需用户自己编译。

为什么需要 node-pre-gyp

当你通过 npm 安装一个包含原生模块的 Node.js 包时,通常需要在本地编译该模块。本地编译需要开发者机器上有编译工具链(如 gcc、clang 或 Visual Studio)。但是,由于以下几个原因,这可能会导致问题:

  1. 用户可能没有必要的编译工具或者对如何安装和使用编译工具不熟悉。
  2. 编译过程可能因为系统配置或依赖项的不同而失败。
  3. 编译原生模块可能会花费相当长的时间。

为了解决这些问题,node-pre-gyp 允许模块维护者提前为各种环境编译好二进制版本,用户在安装时 npm 能够直接下载和使用这些预编译的版本,从而避免了编译过程中的复杂性和耗时。

实际运用例子

假设你正在使用一个名为 sharp 的 Node.js 包,它是一个用于图片处理的库,其中包含了用 C++ 编写的原生代码。如果没有 node-pre-gyp,每个安装 sharp 的人都需要在他们的机器上编译这个原生模块。这可能导致一些用户因为缺少编译器或其他原因而无法成功安装。

但是,由于 sharp 使用了 node-pre-gyp,所以在你执行 npm install sharp 的时候,npm 将查看你的操作系统和 Node.js 版本,然后从 sharp 的服务器上下载适合你系统的预编译二进制文件。整个过程对用户来说是透明的,显著提高了安装速度和成功率。

如何使用 node-pre-gyp

模块维护者通常会在他们的 package.json 文件中添加 node-pre-gyp 相关的配置,并指定好二进制文件的存储位置。在模块安装过程中,npmyarn 会根据这些配置自动调用 node-pre-gyp 来下载或编译原生模块。

总结起来,node-pre-gyp 是 Node.js 生态系统中一个重要的工具,它通过提供预编译的原生模块二进制文件来简化安装过程,改善最终用户的体验。

prebuildopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它允许在服务器端运行 JavaScript 代码。N-API 则是 Node.js 提供的一组 C API,使得原生插件(通常用 C 或 C++ 编写)能够在不同版本的 Node.js 中平稳运行,而不需要因为 Node.js 升级后引擎的变化而重新编译。

那么,“prebuild”这个术语通常与 N-API 和 node-addon-api(一种简化编写原生 Node.js 插件的包装库)有关。"prebuild"指的是事先为不同平台和 Node.js 版本构建好的原生模块(二进制文件)。通过预先构建,模块的安装可以变得更快,因为用户不需要从源代码编译它们。

例如,如果你正在构建一个性能敏感的应用程序,你可能会使用到一些需要利用更深层系统资源优势的代码。你或许会选择写一个用 C++ 编写的模块来处理图像或进行数学计算,比使用 JavaScript 快得多。当用户想要安装你的模块时,你可以提供预先构建好的二进制文件以减少他们的安装时间和复杂度。

现在我们假设你已经创建了这样一个模块,并且你希望它能够跨平台(如 Windows, macOS, Linux)和跨 Node.js 版本工作。你就可以使用类似 prebuildnode-pre-gyp 这样的工具来为这些不同的组合构建二进制文件。

这里有一些具体的步骤示例:

  1. 开发原生模块:首先,你要用 C 或 C++ 开发你的原生模块,并且确保它通过 N-API 接口与 JavaScript 交互。

  2. 配置预构建脚本:配置一个 prebuild 脚本,在你发布模块之前自动为所有目标平台和 Node.js 版本构建二进制文件。

  3. 构建和上传:当你执行 prebuild 脚本时,它会为每个目标平台和 Node.js 版本构建你的模块,并将构建好的二进制文件上传到 GitHub Releases 或其他托管服务上。

  4. 用户安装模块:当用户运行 npm install your-module-name 安装你的模块时,npmyarn 会自动检测用户的平台和 Node.js 版本,并尝试下载相应的预构建二进制文件。如果找到匹配的预构建文件,就直接下载和安装,而不需要从源码编译。如果没有找到匹配的预构建文件,则回退到从源码编译。

实际应用的例子包括数据库驱动(例如 node-sqlite3),加密库(例如 bcrypt),或是数学库(例如 mathjax-node),它们都可能提供预构建的二进制文件,以方便用户安装和使用。

总结来说,“prebuild”在 Node.js 的语境中就是指预先为不同的运行环境编译好的原生模块二进制文件,目的是简化最终用户的安装过程,并提高模块的可移植性和易用性。

prebuildifyopen in new window

Node.js 中的 "prebuildify" 并非 Node.js 的官方 API 的一部分,而是一个独立的 npm 包,用于为 Node.js 的原生模块创建预编译的二进制版本。原生模块是使用 C 或 C++ 编写的插件,可以直接调用 Node.js 底层的 API。正常情况下,当你安装一个包含原生模块的节点包时,这些模块需要在本地机器上从源代码编译。Prebuildify 能够帮助开发者先行在不同的平台上编译好这些原生模块,并且随着 npm 包一起发布。

prebuildify 使用

要使用 prebuildify,通常需要以下步骤:

  1. 确保你有一个需要编译的原生模块项目。
  2. 安装 prebuildify:通过运行 npm install --save-dev prebuildify 将它作为开发依赖添加到你的项目中。
  3. 在 package.json 文件中配置 scripts,增加一个脚本来运行 prebuildify。
  4. 运行配置的脚本以创建预编译的二进制文件。
  5. 发布你的包,这样其他用户在安装时,将会下载相应平台的预编译版本,从而省略了编译过程。

实际例子

假设你正在开发一个名为 "awesome-native-module" 的原生 Node.js 模块。你希望用户在安装时不必从头编译这个模块,以节省时间和避免潜在的编译问题。你可以使用 prebuildify 来实现这一目的。以下是简化的步骤:

  1. 开发你的原生模块,确保它能够在本地编译通过。
  2. 在项目根目录下运行 npm install --save-dev prebuildify 命令,添加 prebuildify 作为开发依赖。
  3. 在 package.json 中添加一个脚本:
    "scripts": {
      "prebuild": "prebuildify --napi"
    }
    
  4. 运行 npm run prebuild。这将为当前系统生成预编译的二进制文件,并将其存储在 ./prebuilds/ 目录中。
  5. 现在你可以发布你的包了。例如,运行 npm publish

如果一切顺利,当用户运行 npm install awesome-native-module 时,npm 会查找与用户系统匹配的预编译二进制文件。如果找到了,它会下载并使用这个预编译的版本而不是从源代码重新编译。

注意: 上述信息可能与 Node.js v21.7.1 版本的具体情况有所差异,因为 Node.js 和相关工具链经常更新。始终建议查阅最新的文档或库说明来获取准确的使用方法。

Usageopen in new window

Node.js 中的 N-API 是一个用于构建本地插件的 API,它是独立于底层 JavaScript 运行时(例如 V8)的抽象层,目的是保持你的插件在 Node.js 的不同版本之间兼容。这意味着使用 N-API 编写的本地插件不需要针对每个新的 Node.js 版本重新编译。

简单来说,如果你想让你的 JavaScript 代码调用 C/C++编写的函数或者库,那么 N-API 可以帮助你做到这点,而不需要担心 Node.js 版本升级后插件不兼容的问题。

以下是使用 N-API 的基本步骤:

  1. 包含头文件:在你的 C 或 C++源文件中,包含<`node_api.h`>头文件,这使得你能够使用 N-API 提供的函数和宏。

    ##include `<`node_api.h>
    
  2. 创建函数:编写你要从 JavaScript 调用的 C/C++函数。这些函数必须遵循 N-API 定义的特定签名。

    napi_value MyFunction(napi_env env, napi_callback_info info) {
        // 函数逻辑
    }
    
  3. 注册函数:使用 N-API 函数注册上面定义的函数,使其可以被 JavaScript 调用。

    napi_value Init(napi_env env, napi_value exports) {
        napi_status status;
        napi_value fn;
    
        // 创建一个napi_value引用,指向我们的原生函数MyFunction
        status = napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
        if (status != napi_ok) {
            // 处理错误...
        }
    
        // 将这个函数作为模块导出的属性
        status = napi_set_named_property(env, exports, "myFunction", fn);
        if (status != napi_ok) {
            // 处理错误...
        }
    
        return exports;
    }
    
    NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
    
  4. 构建和安装:使用node-gyp编译你的 C/C++代码并生成一个.node 文件,这个文件可以被 Node.js 作为模块加载。

  5. 在 Node.js 中使用:在 Node.js 代码中,使用require加载你的本地插件,并像普通模块一样使用。

    const myAddon = require("./build/Release/addon");
    
    console.log(myAddon.myFunction()); // 调用你在C/C++中定义的函数
    

实际运用例子:

  • 性能密集型任务:某些操作在 JavaScript 中执行效率低下,比如图像处理或者大规模数值计算,你可以使用 N-API 编写高效的 C/C++代码来处理这些任务,并通过 Node.js 调用它们。

  • 系统级操作:当你需要进行系统调用或访问操作系统层面的功能,比如硬件信息获取或系统调度,你可以通过 N-API 写本地代码来实现。

  • 集成第三方库:如果你想在 Node.js 项目中使用一些只有 C/C++接口的第三方库,如机器学习库或是数据库驱动,你可以通过 N-API 将它们封装为可从 JavaScript 调用的函数。

N-API 是 Node.js 提供的非常强大的功能,它极大地扩展了 Node.js 的应用场景,并且解决了本地插件与 Node.js 版本兼容性的问题。不过,本地插件开发涉及 C/C++知识,因此会有一定的学习难度。如果你刚开始编程,你可能需要先掌握 JavaScript 和 Node.js 的基础,然后再逐渐深入学习 N-API 相关内容。

Node-API version matrixopen in new window

好的,Node.js 中的 Node-API(之前称为 N-API)是一个让你能够用 C 或 C++ 编写扩展的 API。Node-API 旨在提供一个与 JavaScript 引擎无关的抽象层,这样你就可以编写能在不同版本的 Node.js 上运行的本地插件。

在 Node.js 的每个新版本中,Node-API 也可能会有更新。这些更新可能包括新的 API 函数、性能改进或者其他变化。每个 Node-API 的版本都与特定的 Node.js 版本相对应。为了弄清楚哪些 Node-API 版本可以在特定的 Node.js 版本上使用,Node.js 提供了一个版本矩阵。

下面我将解释如何查看和理解 Node-API 版本矩阵:

  1. 打开 Node-API 版本矩阵链接,你将看到一张表格。
  2. 这张表格列出了不同的 Node-API 版本(即 Node-API v1、v2、v3 等)。
  3. 对于每个 Node-API 版本,表格都会显示它首次引入的 Node.js 版本以及它被废弃(如果已经被废弃)的 Node.js 版本。

例如,假设你查看的版本矩阵如下所示:

Node-API改动
v8Added in: v14.0.0, Removed in: <none>
v7Added in: v13.14.0, Removed in: <none>
v6Added in: v10.20.0, Removed in: <none>

这意味着:

  • Node-API 版本 8 首次引入于 Node.js 版本 14.0.0,并且目前还没有被移除;
  • Node-API 版本 7 首次引入于 Node.js 版本 13.14.0,也尚未被移除;
  • Node-API 版本 6 首次引入于 Node.js 版本 10.20.0,同样也没有被移除。

这样,如果你正在使用 Node.js 版本 14.x,你就可以安全地使用 Node-API 版本 8 或更低的版本来构建你的原生插件。

实际运用的例子:

  1. 比如你想创建一个本地插件来加快你的应用程序的图像处理速度,你的服务器运行的是 Node.js 14.x 版本。根据版本矩阵,你可以使用 Node-API v8 来编写这个插件,因为它可用于 Node.js 14.x。

  2. 假设你需要用到一个现成的数据库插件,但这个插件是基于 Node-API v6 开发的。如果你的服务器运行的是 Node.js 版本 10.20.0 或更高版本,你可以放心地使用该插件。如果你的服务器运行的是更低版本的 Node.js,则需要升级你的 Node.js 版本,或寻找一个兼容当前版本的类似插件。

总结起来,Node-API 版本矩阵帮助开发者理解哪些 Node-API 版本可以在他们的 Node.js 环境中使用,这样他们就可以选择正确的 API 版本来编写或使用原生插件,以确保最佳的兼容性和稳定性。

Environment life cycle APIsopen in new window

Node.js 中的 Environment Life Cycle APIs 是一组专门的 API,它们被用来管理与 Node.js 应用相关联的环境(environment)。每一个 Node.js 运行实例都有一个与之关联的环境,这个环境包含了当前运行代码所需的所有状态和资源。

在 Node.js v21.7.1 中,N-API 提供了一些关于环境生命周期的函数,使得原生模块的开发者可以更好地控制模块与 Node.js 环境之间的交互。原生模块是用 C 或 C++ 编写并编译为共享对象,在 Node.js 中直接加载和运行的模块。

下面我会解释一些关于 Environment Life Cycle 相关的概念,并给出几个简单的例子来说明如何使用这些 API。

1. 创建和销毁环境

当你创建一个 Node.js 应用时,Node.js 会自动创建一个环境。但是,有时候原生模块需要独立于默认环境创建额外的环境。例如,如果你想在一个单独的线程中执行某些代码,你可能需要创建一个新的环境。

创建环境的 API 示例:

napi_env env;
napi_create_environment(..., &env);

销毁环境的 API 示例:

napi_env env;
napi_destroy_environment(env);

2. 环境数据管理

在 Node.js 中,一个环境可以存储和管理许多数据,比如回调函数、对象引用等。通过环境生命周期 API,你可以在环境中添加或移除这些数据。

添加环境数据的 API 示例:

napi_env env;
void* data = ...; // 指向你的数据的指针
napi_add_env_cleanup_hook(env, cleanup_cb, data);

这里 cleanup_cb 是一个回调函数,它会在环境销毁时被调用,以便清理你添加的数据。

移除环境数据的 API 示例:

napi_env env;
void* data = ...; // 指向你的数据的指针
napi_remove_env_cleanup_hook(env, cleanup_cb, data);

这些函数允许你管理模块的资源,确保它们能够在不需要的时候被适当地释放,防止内存泄漏。

实际应用举例

在大多数情况下,Node.js 的 JavaScript 开发者可能不会直接使用这些环境生命周期 API,因为这些操作通常是由原生模块的作者在底层处理的。但是,了解这些 API 可以帮助你理解原生模块是如何工作的。举一些实际的例子:

  • 如果你正在开发一个涉及图像处理的原生模块,你可能需要为每个处理任务创建一个新的环境,并在任务完成后销毁该环境,以确保所有的资源都得到释放。
  • 另一个例子可能是开发数据库连接模块,你可能需要在模块初始化时创建环境,保存数据库连接状态,并在模块卸载时销毁这个环境来关闭所有的数据库连接。

记住,这些 API 对于普通 JavaScript 用户来说是不可见的,只有在编写或维护 Node.js 原生扩展时你才会直接与它们打交道。

napi_set_instance_dataopen in new window

napi_set_instance_data 是 Node.js 中的 N-API 功能之一,N-API 是一个 C API,它允许原生插件(通常用 C 或 C++编写)与 Node.js 交互。使用 napi_set_instance_data 函数可以让你在原生模块中储存特定的数据,并且这些数据是与当前 Node.js 实例(通常指的是运行中的 Node.js 应用程序或进程)绑定的。

现在让我们详细解释一下 napi_set_instance_data 这个函数。

首先,napi_set_instance_data 可以帮助我们保存一些跨原生方法调用保持不变的数据。比如,如果你的原生模块需要一个配置对象,或者需要一个状态来表示某种资源是否已经初始化,那么你就可以使用这个功能。

函数签名如下:

napi_status napi_set_instance_data(
    napi_env env,
    void* data,
    napi_finalize finalize_cb,
    void* finalize_hint);

这个函数接受以下参数:

  • env:是一个代表 Node.js 运行时环境的句柄。
  • data:是你想要存储的数据的指针。
  • finalize_cb:当实例销毁或数据不再需要时,将调用的回调函数,可以用于清理资源。
  • finalize_hint:是一个额外的提示参数,将传递给 finalize_cb 回调函数。

让我们通过一个简单的例子来说明其用法:

假设你正在编写一个原生扩展,需要保存一个全局计数器,每次调用一个特定的函数时,都会增加这个计数器。为了存储和访问这个计数器,你可能会选择使用 napi_set_instance_data

  1. 首先,定义你的计数器和清理函数:
##include `<`node_api.h>

static int counter = 0; // 假设这是你想要全局保存的数据

void cleanup_counter(napi_env env, void* data, void* hint) {
    // 因为我们仅仅保存了一个静态整数,所以这里可能不需要执行任何特殊的清理工作
}
  1. 然后,在模块初始化的时候设置这个数据:
napi_value Init(napi_env env, napi_value exports) {
    // 设置实例数据
    napi_status status = napi_set_instance_data(
        env,
        &counter,
        cleanup_counter,
        NULL);

    if (status != napi_ok) {
        // 处理错误...
    }

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
  1. 接着,每次相关函数被调用时,你可以更新计数器:
void IncrementCounter(napi_env env, napi_callback_info info) {
    // 我们本例中不需要获取 instance data
    // 因为直接访问静态变量 'counter' 就足够了
    counter++;

    // ...其他代码,可能包括返回新的计数器值给JavaScript
}

这样,无论何时调用 IncrementCounter 函数,同一个 counter 就会被更新,因为它是绑定到 Node.js 实例的。

记住,napi_set_instance_data 更多使用场景是当你需要在原生模块中存储指向复杂数据结构(例如动态分配的内存、自定义类型的实例等)的指针时。

此函数适用于需要创建只在单个 Node.js 实例中共享的数据的场景,而不是跨不同加载的模块或不同 Node.js 进程共享。如果你需要跨实例共享数据,则需要寻找其他机制,如使用静态变量或外部存储系统。

napi_get_instance_dataopen in new window

Node.js 中的 N-API(原生 API)是一个用来构建原生插件的接口。原生插件是指使用 C 或 C++ 编写的模块,可直接通过 Node.js 的运行时环境调用。N-API 旨在提供一个与 JavaScript 运行时无关的抽象层,这意味着使用它编写的插件不依赖于特定版本的 Node.js。

napi_get_instance_data 是 N-API 中的一个函数,它允许你从当前的 Node.js 插件实例中获取之前存储的数据。这个功能很有用,因为它能够帮助你维护一些状态信息,而无需将这些信息存储在全局变量中。

当你创建一个原生插件,并且希望跟踪一些与插件实例相关的数据时,napi_get_instance_data 就非常有用了。这个函数可以帮助你检索先前通过 napi_set_instance_data 函数设置的数据。

使用场景举例

  1. 计数器插件:假设你正在编写一个原生插件,该插件维护一个简单的计数器。每次调用插件函数时,计数器都会递增。为了跟踪这个计数器,你可以使用 napi_set_instance_data 在初始化时存储它,并在后续的函数调用中使用 napi_get_instance_data 来获取当前的计数值。

  2. 数据库连接:如果你的插件负责管理数据库连接,则可以在插件初始化时创建一个数据库连接,并使用 napi_set_instance_data 存储该连接的引用。随后,每当需要执行数据库操作时,就可以通过 napi_get_instance_data 获取到这个数据库连接,并使用它进行查询或更新操作。

  3. 配置信息:如果你的插件需要加载和存储一些配置信息,那么可以在插件启动时读取这些配置,并通过 napi_set_instance_data 存储起来。以后每次插件需要这些配置信息时,都可以通过 napi_get_instance_data 来获取。

示例代码片段

以下是一个使用 napi_get_instance_data 的伪代码示例:

##include `<`node_api.h>

// 假设我们有一个结构体来保存我们的实例数据
typedef struct {
  int counter; // 用于示例的计数器
} MyInstanceData;

// 函数用于获取实例数据
napi_value GetCounter(napi_env env, napi_callback_info info) {
  MyInstanceData* instance_data;

  // 通过 napi_get_instance_data 获取实例数据
  napi_status status = napi_get_instance_data(env, (void**)&instance_data);
  if (status != napi_ok) {
    // 处理错误情况...
  }

  // 现在你可以访问 instance_data->counter 并根据需要操作它了

  // 返回计数器的当前值作为 JavaScript 数字
  napi_value result;
  status = napi_create_int32(env, instance_data->counter, &result);
  if (status != napi_ok) {
    // 处理错误情况...
  }

  return result;
}

// 该函数可能会在插件初始化时被调用,它会设置实例数据
void Initialize(napi_env env, napi_value exports) {
  MyInstanceData* instance_data = malloc(sizeof(MyInstanceData));
  instance_data->counter = 0; // 初始化计数器

  // 使用 napi_set_instance_data 存储实例数据
  napi_status status = napi_set_instance_data(
    env,
    instance_data,
    NULL, // 这里可以提供一个析构函数,用来在插件卸载时清理资源
    NULL
  );
  // 确保 status == napi_ok,否则处理错误情况
}

这段代码展示了如何使用 napi_get_instance_data 函数在一个原生插件中获取实例数据。记住,为了使代码正常工作,你还需要实现其他一些部分,比如插件的注册逻辑及其它必要的错误处理。

Basic Node-API data typesopen in new window

Node-API(以前称为 N-API)是 Node.js 的一个接口层,它允许你使用 C 或 C++ 编写扩展来和 JavaScript 代码交互。Node-API 设计的目标是减少与底层 JavaScript 引擎的直接交互,这样编写的扩展就能够在 Node.js 的不同版本之间保持兼容。

在 Node-API 中有一些“基本数据类型”,这些数据类型代表了 JavaScript 值和类型在 C/C++ 层面上的等效表示。理解这些基本数据类型对于编写可靠、高效的原生扩展非常重要。

以下是一些 Node-API 中的基本数据类型及其简单描述:

  1. napi_value: 代表一个 JavaScript 值。无论是个数字、字符串还是对象,在 C/C++ 层面上都用 napi_value 表示。
  2. napi_env: 代表一个 Node-API 调用环境,所有的 Node-API 函数调用都需要这个环境作为参数之一,因为它包含了 Node-API 操作的上下文信息。
  3. napi_callback_info: 当 JavaScript 调用一个由 C/C++ 提供的函数时,会将相关信息打包进一个 napi_callback_info 类型中,比如函数被调用时的参数。
  4. napi_ref: 代表一个对 JavaScript 对象的强引用,这可以防止该对象在它仍然被 C/C++ 扩展使用时被垃圾回收器回收。
  5. napi_handle_scope: 管理局部 napi_value 句柄的生命周期,用于控制内存分配,确保在当前作用域结束时释放局部句柄。
  6. napi_escapable_handle_scope: 类似 napi_handle_scope,但允许一个句柄从一个作用域“逃逸”到父作用域。
  7. napi_callback_scope: 与正在执行的 JavaScript 函数关联的作用域。
  8. napi_async_work: 表示异步工作的句柄,通过这个可以在后台线程上执行任务而不会阻塞 JavaScript 主线程。
  9. napi_deferred: 代表一个延迟的操作,通常与 Promise 结合使用,允许从原生代码中创建可解析或拒绝的 Promise 对象。

实例运用:

假设你想在 Node.js 中创建一个原生模块,用来加密字符串。你可能需要编写一个 C 功能来完成加密工作,并且利用 Node-API 把这个功能暴露给 JavaScript。

##include `<`node_api.h>

// 实际的加密函数
void EncryptString(napi_env env, napi_callback_info info) {
    // 获取函数参数
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 将 napi_value 转换为 C 字符串
    char* originalStr;
    size_t str_len;
    napi_get_value_string_utf8(env, args[0], NULL, 0, &str_len);
    originalStr = (char*) malloc(str_len + 1);
    napi_get_value_string_utf8(env, args[0], originalStr, str_len + 1, &str_len);

    // 这里是加密操作(省略实现)

    free(originalStr);

    // 创建返回值
    napi_value returnValue;
    napi_create_string_utf8(env, "encrypted string", NAPI_AUTO_LENGTH, &returnValue);

    return returnValue;
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;
    napi_create_function(env, NULL, 0, EncryptString, NULL, &fn);
    napi_set_named_property(env, exports, "encrypt", fn);
    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面这段代码中,我们定义了一个名为 EncryptString 的 C 函数作为 Node-API 接口。这个函数通过 napi_get_cb_info 从 JavaScript 世界获取参数,并且将 JavaScript 字符串转换为 C 字符串。之后,我们执行一个假设的加密操作,最后创建一个新的 JavaScript 字符串(加密结果)并返回。

最终,这个原生模块通过 Init 函数(作为模块初始化入口点)在 Node.js 中注册了 encrypt 函数。JavaScript 开发者就可以像使用普通的 JavaScript 函数一样,使用 require 导入这个模块,并调用其中的 encrypt 函数进行字符串加密。

napi_statusopen in new window

Node.js 中的 N-API(Node.js API)是一个用于构建原生插件的稳定级别的 API。它允许你不直接使用 V8 或其他 JavaScript 引擎的 API,而是使用一组固定的 C API 来编写可跨不同版本的 Node.js 运行的原生代码。

在 N-API 中,napi_status 是一个枚举类型,它代表了 N-API 函数调用的返回状态,即函数执行成功或失败的状态码。每个 N-API 调用都会返回一个 napi_status 值,以指示操作是否成功,以及如有错误,其性质为何。

以下是一些例子来解释 napi_status 的不同值:

  1. napi_ok: 表示调用成功完成,没有错误发生。
  2. napi_invalid_arg: 表示传递给函数的某个参数无效。
  3. napi_object_expected: 当 API 期望一个对象,但没有收到对象时返回这个状态。
  4. napi_string_expected: 当 API 期望一个字符串,但得到的不是字符串时返回这个状态。
  5. napi_name_already_registered: 当尝试注册已经存在的名称时返回这个状态码。
  6. napi_generic_failure: 表示出现了未分类的通用错误。
  7. napi_pending_exception: 表示 JavaScript 引擎中存在待处理的异常,阻塞了 N-API 调用的正常执行。

这些状态码对开发者来说很重要,因为它们提供了关于原生插件中发生什么的直接反馈,并可以据此采取相应的错误处理措施。

实际运用的例子

假设我们正在创建一个原生插件,其中包括一个需要从 JavaScript 传入一个字符串进行处理的函数。我们可以检查传入的参数并根据其状态采取行动:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1];
    napi_status status;

    // 获取JavaScript传递的参数
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
    if (status != napi_ok) {
        // 处理获取参数时出现的错误
        // ...
    }

    // 确保传入的是字符串
    napi_valuetype valuetype;
    status = napi_typeof(env, argv[0], &valuetype);
    if (status != napi_ok) {
        // 处理获取参数类型时出现的错误
        // ...
    }

    if (valuetype != napi_string) {
        // 抛出错误: 我们期望一个字符串
        napi_throw_type_error(env, NULL, "String was expected");
        return NULL;
    }

    // 如果一切正常,则继续处理字符串...
    // ...

    // 返回一些结果
    napi_value result;
    // ...
    return result;
}

// 模块初始化函数
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

在这个例子中,我们首先调用 napi_get_cb_info 来获取从 JavaScript 传递的参数。如果出错,我们可以根据返回的 napi_status 来确定错误的类型并采取相应的措施。然后我们检查传入参数的类型是否为字符串,如果不是,则抛出类型错误。

通过这样的机制,开发者能够以稳健的方式构建原生扩展,确保正确地处理输入并在出现问题时提供有用的错误信息。

napi_extended_error_infoopen in new window

Node.js 中的 N-API 是一个 C 级别的 API,它允许本地插件(通常用 C 或 C++编写)与 JavaScript 代码通信。N-API 旨在为构建这些本地插件提供稳定和兼容不同版本的 Node.js 的接口。

napi_extended_error_info结构体是 N-API 中的一个重要概念,它提供了关于发生错误的详细信息。当 N-API 函数返回错误时,这个结构体会包含有助于调试和错误处理的附加信息。

下面我会解释什么是napi_extended_error_info,并举一些可能的应用场景:

napi_extended_error_info

napi_extended_error_info 是一个结构体(C 语言中的一种数据类型,可以包含多个其他类型的变量),它定义了与 N-API 操作相关的错误详情。这个结构体里通常包含以下字段:

  • const char* error_message: 这是一个指向错误消息字符串的指针。错误消息提供了问题的描述。
  • void* engine_reserved: 这是保留给 JavaScript 引擎自身使用的数据;对于插件开发者来说通常是不可见的。
  • void* engine_error_code: 这是一个指向特定于引擎的错误代码的指针。
  • uint32_t error_code: 这是 N-API 的错误码,它表示错误的具体类型。

实际运用例子

假设你正在编写一个本地插件来提高应用程序性能,例如,一个压缩图片的插件。你可能会用 C 或 C++编写一个函数来处理图片文件,并通过 N-API 将其暴露给 Node.js。

当你在插件内调用一个 N-API 函数时,比如napi_create_string_utf8来创建一个新的 JavaScript 字符串,这个调用可能因为各种原因失败(例如,如果系统内存不足)。如果这个函数返回一个表示失败的状态码,你可以获取napi_extended_error_info结构体来查看错误详情:

##include `<`node_api.h>
// ... 省略其他必要的头文件和代码 ...

napi_value CreateString(napi_env env, napi_callback_info info) {
  // 尝试创建一个新的字符串 "hello"
  napi_value result;
  napi_status status = napi_create_string_utf8(env, "hello", NAPI_AUTO_LENGTH, &result);

  // 检查函数是否成功执行
  if (status != napi_ok) {
    // 获取错误信息
    const napi_extended_error_info* error_info;
    napi_get_last_error_info(env, &error_info);

    // 打印错误信息到控制台
    printf("Failed to create string: %s\n", error_info->error_message);

    // 抛出一个JavaScript异常
    napi_throw_error(env, NULL, error_info->error_message);
    return NULL;
  }

  return result;
}

在上面的代码示例中,我们首先尝试使用napi_create_string_utf8创建一个节点字符串。如果这个尝试失败了,我们通过napi_get_last_error_info获取关于这个错误的更多信息,然后打印出错误消息,并且抛出一个 JavaScript 异常来告诉 JavaScript 代码发生了错误。

通过napi_extended_error_info所提供的信息,作为插件作者,你可以更好地理解错误发生的背景,并且提供更多的上下文信息给使用你插件的开发者。这使得调试本地插件和处理错误成为一件更加可控和清晰的工作。

napi_envopen in new window

napi_env是一个代表 Node.js 运行环境的抽象概念,它在 Node.js 的 N-API(Native API)中非常重要。N-API 是一种 C 语言级别的 API,允许开发人员编写可以与 Node.js 代码交互的本地插件,这些插件通常是用 C 或 C++写成的,并且可以直接调用 Node.js 提供的各种 API。

napi_env 是一个指向环境相关数据的指针,你可以将其理解为一个包含了当前 Node.js 实例信息的上下文对象。在 N-API 中进行本地插件开发时,几乎所有的函数都需要这个环境指针来确认它们正在操作的是哪一个 Node.js 实例。

为什么要有 napi_env

  • 多线程支持: Node.js 是单线程的,但通过 worker threads 可以运行多个线程。每个线程都会有自己的napi_env环境,这样就能确保操作不会冲突。
  • 模块实例化: 当你有多个模块实例时,每个实例都有自己的napi_env来存储状态和上下文信息。

以下是几个使用napi_env的简单示例:

  1. 创建一个新的 JavaScript Number: 为了在本地插件中创建一个新的 JavaScript 数字并返回给 JavaScript,你会使用带有napi_env参数的 N-API 函数,如下所示:

    napi_status status;
    napi_value my_number;
    status = napi_create_double(env, 123.456, &my_number);
    if (status != napi_ok) {
      // 处理错误...
    }
    
  2. 从 JavaScript 接收参数: 假设你正在编写一个本地函数,该函数需要从 JavaScript 端获取参数,你会这样使用napi_env

    napi_value function_callback(napi_env env, napi_callback_info info) {
      size_t argc = 1;
      napi_value argv[1];
      napi_status status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
      if (status != napi_ok) {
        // 处理错误...
      }
    
      // 假设我们知道argv[0]是一个数字
      double value;
      status = napi_get_value_double(env, argv[0], &value);
      if (status != napi_ok) {
        // 处理错误...
      }
    
      // 现在可以使用这个值做一些操作...
    }
    
  3. 抛出异常: 如果在本地插件的执行过程中发生错误,你可能需要抛出一个异常,让 JavaScript 端能够捕获到。你将使用napi_env创建一个错误对象并抛出它:

    napi_throw_error(env, NULL, "An error occurred!");
    

这些只是napi_env在 N-API 中应用的基础示例,但它们展示了napi_env作为连接 C/C++代码和 Node.js 运行环境的桥梁的基本用途。在实际开发中,napi_env用于确保你的本地插件可以正确地在 Node.js 环境中运行,管理资源,以及处理异步操作等等。

node_api_nogc_envopen in new window

node_api_nogc_env 是 Node.js 的 N-API 中的一个特性,它允许原生模块的开发者创建一个不会被垃圾回收器(GC)干预的环境。在详细解释之前,我们先来弄明白几个基本概念。

什么是 N-API?

N-API 是 Node.js 提供的一套 C 语言 API 接口,它允许你用 C 或 C++编写扩展模块,这些模块可以直接与 Node.js 运行时交互。使用 N-API 编写的模块是与 Node.js 版本无关的,这意味着你编写一次代码,理论上在任何支持 N-API 的 Node.js 版本上都能运行而无需修改。

垃圾回收器(GC)是什么?

在 JavaScript 中,当变量或者对象不再需要时,垃圾回收器会自动释放那些内存空间。这是自动进行的,通常开发者不需要手动管理内存。不过,在原生模块中,你可能会创建一些由 C/C++ 管理的资源,这些资源不是 JavaScript 垃圾回收器直接管理的。

node_api_nogc_env 的作用

当使用 N-API 编写原生模块时,某些情况下你可能需要确保在执行某段关键的代码时,垃圾回收器不要运行。因为如果在这些代码执行期间发生了垃圾回收,可能会导致性能问题或者更糟的情况是错误和崩溃。

使用 node_api_nogc_env 可以创建一个“无 GC 环境”,在这个环境中执行代码时,Node.js 的垃圾回收器会被暂时阻止执行。这对于控制并优化性能非常有用,尤其是在处理实时数据或高性能计算时。

举个例子:

假设你正在编写一个需要与硬件设备通信的 Node.js 原生模块。这个通信过程可能涉及精确的时序和快速的数据处理。在这种情况下,如果代码执行期间发生垃圾回收,可能会导致数据丢失或时序问题。所以,在设置通信协议或处理数据的核心代码部分,你可能会希望使用 node_api_nogc_env 来确保此时不会发生垃圾回收。

// 假设的代码示例
##include `<`node_api.h>

napi_value CommunicateWithDevice(napi_env env, napi_callback_info info) {
    // 创建一个无GC环境。
    napi_env nogc_env;
    napi_create_env(env, &nogc_env);

    // 在此环境中执行关键操作...
    // 例如:发送指令到硬件设备,处理返回的数据等操作。

    // 销毁无GC环境
    napi_destroy_env(nogc_env);

    return nullptr; // 返回值
}

// 注册函数
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, CommunicateWithDevice, NULL, &fn);
    napi_set_named_property(env, exports, "communicateWithDevice", fn);
    return exports;
}

请注意,这只是一个概念性的示范,实际的 N-API 用法可能会有所不同。

重要的是要明白,node_api_nogc_env 应该谨慎使用。阻止垃圾回收器运行可能会导致内存使用随时间增加,因为没有清理不再需要的对象。因此,通常只在必须最大化性能,且了解你的内存管理非常好的情况下,才使用这个特性。

napi_valueopen in new window

napi_value 是 Node.js 中的 N-API(Node API)的一个概念。N-API 是一个 C 语言接口,它提供了一种建立稳定和兼容不同 Node.js 版本的原生插件(native addons)的方法。在解释 napi_value 前,我们需要先了解几个关键点:

  1. 原生插件 (Native Addons):这些是用 C 或 C++编写的模块,可以直接调用 Node.js 运行时以外的 API,例如直接操作系统底层功能或访问特定硬件。

  2. N-API:为了使原生插件更加稳定并减少与 Node.js 版本更新相关的异变性,Node.js 提供了 N-API,它定义了一套跨版本稳定的 API。

  3. JavaScript 值与 C 类型的映射:由于 N-API 是用 C 编写的,而 Node.js 的运行环境是基于 JavaScript 的,所以必须有一种方式来在 JavaScript 世界和 C 语言世界之间转换数据。

现在,让我们深入 napi_value

napi_value

在使用 N-API 编写原生模块时,你会处理很多 JavaScript 值(如数字、字符串、对象等)。napi_value 是一个抽象表示,它代表了一个 JavaScript 值在 C 语言层面的引用或句柄。每当你想在 C/C++代码中操作一个 JavaScript 值时,你实际上是通过 napi_value 这个抽象层来进行操作。

napi_value 只是一个指针(或者说是一个引用),它没有告诉你具体的值是什么,但它可以被传递到其他 N-API 函数中去创建、查询或操作那个 JavaScript 值。

实际例子

假设你想写一个原生插件函数,将两个 JavaScript 数字相加,并返回结果。以下是可能的 C 代码片段:

##include `<`node_api.h>

// 函数实现,用于加法操作
napi_value Add(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 2;
    napi_value args[2];
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查参数数量和类型等...

    double value1, value2;
    status = napi_get_value_double(env, args[0], &value1);
    status = napi_get_value_double(env, args[1], &value2);

    napi_value sum;
    status = napi_create_double(env, value1 + value2, &sum);

    return sum;
}

// 注册Add函数到module.exports
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    status = napi_create_function(env, NULL, 0, Add, NULL, &fn);
    status = napi_set_named_property(env, exports, "add", fn);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中:

  • Add 函数就是我们要用 N-API 暴露给 JavaScript 的 C 函数。
  • args 数组包含着函数调用时传递进来的 JavaScript 参数。
  • 每一个 args 元素都是一个 napi_value 类型,表示 JavaScript 传递的值。
  • 使用 napi_get_value_double 函数从 napi_value 中提取出 C 语言中的 double 类型的数值。
  • napi_create_double 创建了一个新的 JavaScript Number 值,并返回其 napi_value 表示。
  • 最后,Add 函数通过返回这个新创建的 napi_value 将结果传回 JavaScript。

使用以上代码,你在 JavaScript 中就可以像下面这样调用该插件:

const nativeAddon = require("./build/Release/nativeAddon.node");
console.log(nativeAddon.add(5, 10)); // 输出: 15

napi_value 是 N-API 编程中的核心概念,因为它是 JavaScript 世界和 C 世界互动的桥梁。通过理解和使用 napi_value,你可以构建强大的 Node.js 原生扩展,充分发挥 JavaScript 和系统底层能力的结合优势。

napi_threadsafe_functionopen in new window

Node.js 中的napi_threadsafe_function是一个非常强大的功能,它允许你从任何线程安全地调用 JavaScript 函数。在了解这个概念之前,我们需要先理解几个基础点:

  1. Node.js 的单线程模型:Node.js 通常运行在单个线程上,这意味着所有的 JavaScript 代码都在同一个线程(主线程)上执行。但是,有时候我们可能需要执行一些耗时的操作,比如读写文件、网络请求或者其他复杂计算,这些操作如果放在主线程上执行,会阻塞其他代码的执行。

  2. 异步和事件循环:为了解决上述问题,Node.js 采用非阻塞 I/O 和事件循环机制来进行异步编程。这样,一些耗时的操作可以在后台执行,执行完毕后通过回调函数来通知主线程。

  3. 多线程和worker_threads:尽管 Node.js 主要是单线程的,但它也支持使用worker_threads模块创建真正的多线程。这在处理 CPU 密集型任务时非常有用,因为可以创建工作线程来分担计算负荷。

现在,当你在工作线程(或者任何非主线程)中进行操作,并且想要与主线程中的 JavaScript 代码交互时,就需要用到napi_threadsafe_function了。

napi_threadsafe_function 使用场景和例子

假设你在一个 Node.js 应用程序中运行了一个工作线程来执行一个耗时的图像处理任务。完成这个任务后,工作线程需要将结果传送回主线程,以便更新用户界面或者发送给客户端。这里就可以使用napi_threadsafe_function来实现安全的通信。

例子 1:

const { Worker, isMainThread, parentPort } = require("worker_threads");

if (isMainThread) {
  // 主线程代码
  const worker = new Worker(__filename);

  // 接收工作线程发来的消息
  worker.on("message", (msg) => {
    console.log("收到:", msg);
  });

  worker.on("error", (err) => {
    console.error(err);
  });

  worker.on("exit", (code) => {
    if (code !== 0) console.error(`工作线程停止,退出码: ${code}`);
  });
} else {
  // 工作线程代码
  parentPort.postMessage("工作线程发送的数据");
}

在这个简化的例子中,我们没有直接使用napi_threadsafe_function,因为worker_threads模块已经对其进行了封装。工作线程通过parentPort.postMessage发送消息,主线程通过监听message事件来接受消息。

例子 2:使用napi_threadsafe_function的原生扩展

如果你正在编写一个需要在 C++中创建新线程,并且需要和 JS 通信的 Node.js 原生扩展,那么你就需要直接使用napi_create_threadsafe_function

由于这涉及到较复杂的 C++代码和 Node.js 的 N-API 接口,以下只提供一个高层次的概念介绍:

##include `<`node_api.h>

// C++ 线程执行的函数
void MyThreadFunction(napi_threadsafe_function tsfn) {
    // 执行一些任务...

    // 调用 JavaScript 函数
    napi_call_threadsafe_function(tsfn, ...);

    // 任务完成,释放函数
    napi_release_threadsafe_function(tsfn, napi_tsfn_release);
}

// 初始化 threadsafe function 并创建线程
napi_value Init(napi_env env, napi_callback_info info) {
    napi_value js_func;
    // 获取 JavaScript 函数参数...

    // 创建一个 threadsafe function
    napi_create_threadsafe_function(env, js_func, ..., MyThreadFunction, ..., &tsfn);

    // 创建并启动一个新线程...
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中,napi_create_threadsafe_function用于创建一个可以跨线程安全调用的 JavaScript 函数。C++线程可以使用这个函数来安全地回调 JavaScript 函数,而不会造成并发问题。

总结起来,napi_threadsafe_function是 Native Addons 在多线程环境下与 JavaScript 代码交互的桥梁。对于纯 JavaScript 开发者而言,通常不需要直接使用它,因为worker_threads模块已经提供了更高级别的抽象。

napi_threadsafe_function_release_modeopen in new window

napi_threadsafe_function_release_mode 是 Node.js 中 N-API 的一部分,N-API 是一个用于构建原生插件的 API。在深入理解 napi_threadsafe_function_release_mode 之前,让我们先了解一些基础概念。

什么是 N-API?

N-API 是 Node.js 提供的一个抽象层,允许你使用 C 或 C++ 编写可以和 JavaScript 代码交互的原生插件。这样,你就能够在本地代码中执行高性能或系统级别操作,然后通过 JavaScript 在 Node.js 环境中调用这些操作。

什么是 Thread-safe Function(线程安全函数)?

在多线程编程中,"线程安全"意味着在多个线程同时访问同一资源时,程序能够正确地管理对该资源的访问,以避免不可预料的结果或崩溃。当你从原生线程访问 Node.js 运行时或者想要在原生线程中调用 JavaScript 函数时,你会需要这种线程安全机制。

napi_threadsafe_function_release_mode 的作用:

napi_threadsafe_function_release_mode 是一个枚举值,它描述了在释放(结束)一个线程安全函数时应该采取的行为模式。当你完成了对线程安全函数的使用,并且想要清理资源,通知 Node.js 这个函数不再被原生代码使用时,你会调用相关的 N-API 函数来释放这个线程安全函数。

Node.js 提供了两种释放模式:

  1. napi_tsfn_release:这是默认的释放模式。当你调用这个选项时,Node.js 将等待所有已经提交给线程安全函数的任务都完成才会销毁这个函数。

  2. napi_tsfn_abort:如果你选择这个模式,Node.js 将立即停止接受新的任务,并尽可能快地销毁这个函数。这个模式适合于那些由于某些原因需要立即终止的情况。

实际运用例子:

假设你正在开发一个 Node.js 应用程序,该程序需要处理图像。

  1. 普通模式 (napi_tsfn_release)

    • 你创建了一个线程安全函数,让其调用一个 C++ 函数来处理图像。
    • 随着用户上传更多的图片,你的应用程序将这些图片推送到原生队列中。
    • 最终用户停止上传图片,你决定关闭应用程序。
    • 在关闭前,你使用 napi_tsfn_release 模式释放线程安全函数,确保所有排队的图像都得到处理,然后再关闭。
  2. 中止模式 (napi_tsfn_abort)

    • 同样的情况,但突然间你的应用程序需要紧急关闭(可能是因为内存泄露或其他关键错误)。
    • 在这种场景下,你不能等待所有的图片都处理完成,因此你使用 napi_tsfn_abort 来快速释放线程安全函数,然后关闭应用程序。

通过使用合适的释放模式,你可以更好地控制如何安全地结束与 JavaScript 运行时的交互,无论是有序地完成所有任务还是紧急中断。

napi_threadsafe_function_call_modeopen in new window

napi_threadsafe_function_call_mode 是 Node.js 中 N-API 的一个枚举类型,它用于指定调用线程安全函数时的行为模式。在解释这个概念之前,让我们先了解一些背景知识。

Node.js 是单线程的,这意味着它默认情况下只使用一个线程来执行所有的 JavaScript 代码。然而,在某些情况下,你可能需要执行一些耗时的任务,比如读取大文件、进行复杂计算或者访问数据库等。在这种情况下,如果在主线程中执行这些耗时的操作,会造成应用响应缓慢,影响用户体验。

为了解决这个问题,Node.js 提供了 Worker 线程(通过 worker_threads 模块),允许你创建额外的线程来处理这些耗时任务,而不会阻塞主线程。

然而,当你想要从这些新创建的 Worker 线程与主线程(或者其他 Worker 线程)通信时,你需要一个安全的方式来交换数据和发送消息。这就是 napi_threadsafe_function 函数发挥作用的地方:它允许你创建一个可以从任何线程安全地调用的 JavaScript 函数。

现在,让我们回到 napi_threadsafe_function_call_mode。这个枚举有两个值,它们控制当你尝试调用线程安全函数时的行为:

  1. napi_tsfn_blocking: 如果队列已满,该模式会使调用者阻塞(等待),直到队列有空位让调用者插入它的请求。
  2. napi_tsfn_nonblocking: 如果队列已满,该模式会直接返回一个错误,不会阻塞调用者。

实际运用的例子

假设你正在编写一个 Node.js 应用,需要从多个网络资源下载数据。每个下载可能会花费较长时间,所以你决定在 Worker 线程中执行下载任务。

const { Worker, isMainThread, parentPort } = require("worker_threads");

if (isMainThread) {
  // 主线程代码
  const worker = new Worker(__filename);

  // 监听来自 Worker 的消息
  worker.on("message", (msg) => {
    console.log("Received from worker:", msg);
  });
} else {
  // Worker 线程代码

  // 模拟一些耗时的操作
  setTimeout(() => {
    parentPort.postMessage("Download complete");
  }, 2000);
}

在上面这个例子中,我们创建了一个 Worker 线程来模拟一个耗时的下载操作。然后,当下载完成后,Worker 发送一个消息回主线程。

现在,让我们改进这个例子,假设我们的下载函数是一个原生的 C++ 插件,并使用 N-API 创建线程安全函数来从 Worker 线程中通知主线程下载完成。

// 假设这是一个 C++ 插件的代码片段
napi_value DownloadComplete(napi_env env, napi_callback_info info) {
  // 获取线程安全函数对象,并调用它
  napi_status status = napi_call_threadsafe_function(my_tsfn, "Download complete", napi_tsfn_blocking);
  if (status != napi_ok) {
    // 处理错误...
  }
  return nullptr;
}

在这个 C++ 代码片段中,my_tsfn 是之前创建的线程安全函数对象。我们尝试用 napi_call_threadsafe_function 调用它,并传递 "Download complete" 字符串作为消息。我们使用 napi_tsfn_blocking 模式,因为我们希望在队列满时等待,直到有空间可以发送消息。

如果我们想要非阻塞行为,我们可以选择 napi_tsfn_nonblocking 模式,这样如果队列满了,函数将立即返回一个错误,我们可以据此决定是否重试或采取其他措施。

Node-API memory management typesopen in new window

Node-API(之前称为 N-API)是 Node.js 的一个部分,它提供了一个稳定且独立于 JavaScript 运行时的 API,使得我们可以构建原生模块。这些原生模块是用 C 或 C++编写的,并且可以被 Node.js 直接调用。Node-API 保证了跨不同版本的 Node.js 二进制兼容性,这意味着原生模块不需要针对每个新版本的 Node.js 重新编译。

让我们来谈谈在 Node-API 中涉及内存管理的类型。在 Node-API 中,你会遇到几种与内存管理相关的类型和方法,这些都是为了确保资源有效地使用,防止内存泄漏。以下是其中一些主要的概念:

  1. napi_value: 这是一个表示 JavaScript 值的抽象类型,在 Node-API 函数中经常使用。当你创建一个新的 JavaScript 对象、字符串或任何其他 JS 值时,Node-API 通常会返回一个napi_value

  2. 引用(Reference): 在 Node-API 中,引用是一个指向napi_value的指针,它确保了即使在本地代码执行完成后,该值仍然不会被垃圾回收器回收。在 Node-API 中,你可以创建弱引用或强引用。

    • 强引用:保持了对napi_value的一个活跃的引用,这意味着只要引用存在,垃圾回收器就不会释放该值。这在你需要长期持有一个值时非常有用。

    • 弱引用:不保证napi_value的生命周期。垃圾回收器可以随时释放掉相关联的值。这对于允许内存释放,同时依然能够偶尔访问值的场景很有用。

  3. napi_finalize: 这是一个回调函数,当一个与napi_value相关联的对象被垃圾回收器回收时,这个函数会被调用。它允许你进行清理操作,比如释放在原生层分配的内存。

现在,让我们举一个例子来说明这些概念:

假设你正在创建一个 Node.js 原生模块,该模块包含了一个原生 C++对象。你想要在 JavaScript 层次上暴露这个对象,并且想要在该对象不再需要时自动清理它所占用的资源。

##include `<`node_api.h>

// 假设这是你的C++对象
class MyObject {
public:
    MyObject() { /* 构造函数逻辑 */ }
    ~MyObject() { /* 析构函数逻辑,例如释放分配的内存 */ }
};

// 这是当JS对象被垃圾回收器回收时,将被调用的回调函数
void FinalizeCallback(napi_env env, void* finalize_data, void* finalize_hint) {
    // 将finalize_data转型回MyObject指针并删除它
    MyObject* obj = static_cast`<`MyObject*>(finalize_data);
    delete obj;
}

// 这是一个Node-API函数,它创建并返回一个新的MyObject实例的封装
napi_value CreateMyObject(napi_env env, napi_callback_info info) {
    // 创建一个新的C++ MyObject实例
    MyObject* obj = new MyObject();

    // 创建一个新的空JS对象
    napi_value js_obj;
    napi_create_object(env, &js_obj);

    // 将C++对象与JS对象关联,并设置FinalizeCallback作为析构回调
    napi_wrap(env, js_obj, obj, FinalizeCallback, nullptr, nullptr);

    // 返回创建的JS对象
    return js_obj;
}

// 你需要将CreateMyObject函数暴露给JavaScript,这样你就可以在JS代码中调用它

在这个例子中,你创建了一个 C++对象,并通过 Node-API 将其封装在一个 JavaScript 对象中。你还设置了一个FinalizeCallback回调,当 JavaScript 对象被垃圾回收时,这个回调会被触发以清理 C++对象。这样,你就实现了在原生模块和 JavaScript 之间的桥梁,同时确保了良好的内存管理。

napi_handle_scopeopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。在 Node.js 中,JavaScript 通常运行在 V8 引擎上,而 N-API 允许开发者使用 C 或 C++ 代码与 JavaScript 进行交互。

napi_handle_scope 是 N-API 中的一个概念,它和垃圾回收机制有关。在 JavaScript 中,当你创建了很多对象(比如字符串、数组、对象等)以后,这些对象会占用内存。为了释放不再需要的对象所占用的内存,V8 引擎会定期执行垃圾回收。

当你在使用 N-API 编写原生模块时,可能会创建很多和 JavaScript 交互的对象。napi_handle_scope 就是用来管理这些对象的生命周期。每个 napi_handle_scope 都是一系列 N-API 对象的上下文环境,N-API 会负责跟踪在这个作用域中创建的所有对象,并在这个作用域结束时,确保这些对象能够被垃圾回收器正确地处理。

使用 napi_handle_scope 的实际例子:

假设你正在编写一个 Node.js 的原生模块,这个模块需要创建一个新的 JavaScript 数组,并在这个数组中填充一些数字。你可以这样做:

##include `<`node_api.h>

// 这个函数是用来创建并返回一个新的 JavaScript 数组,数组中包含了从0到9的数字。
napi_value CreateArrayWithNumbers(napi_env env) {
    napi_status status;

    // 创建一个新的 handle scope
    napi_handle_scope scope;
    status = napi_open_handle_scope(env, &scope);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 创建一个新的 JavaScript 数组
    napi_value array;
    status = napi_create_array_with_length(env, 10, &array);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 填充数组
    for (int i = 0; i `<` 10; ++i) {
        napi_value num;
        status = napi_create_int32(env, i, &num);
        if (status != napi_ok) {
            // 处理错误...
        }

        // 把数字设置到数组对应的索引上
        status = napi_set_element(env, array, i, num);
        if (status != napi_ok) {
            // 处理错误...
        }
    }

    // 关闭 handle scope,在这之后,我们创建的所有本地对象都可以被垃圾回收了
    status = napi_close_handle_scope(env, scope);
    if (status != napi_ok) {
        // 处理错误...
    }

    return array;
}

在上面的例子中,我们首先打开了一个 napi_handle_scope,然后执行了创建数组和填充数组的操作。在操作完成后,我们关闭了 napi_handle_scope。关闭操作告诉 N-API,我们在这个作用域中创建的所有对象在这之后都不再需要了,因此它们可以被垃圾回收机制回收。

简而言之,napi_handle_scope 被用于在编写原生模块时管理对象的生命周期,确保它们能在合适的时候被垃圾回收,以此避免内存泄露。

napi_escapable_handle_scopeopen in new window

当然,我会用尽可能简单的语言来解释 napi_escapable_handle_scope

首先,要了解 napi_escapable_handle_scope 的概念,我们需要先理解 Node.js 中的 N-API 以及它为何存在。

N-API 是什么? N-API 是 Node.js 提供的一个稳定的原生(C/C++)API 层。它允许原生模块(native modules)开发者编写与 Node.js 版本无关的代码,这就意味着一旦你编写了使用 N-API 的原生模块,它可以在未来的 Node.js 版本中运行,而不需要重新编译。

什么是 Handle Scope? 在 Node.js 中,JavaScript 和原生代码(比如 C/C++ 代码)之间有一个桥梁,那就是 V8 引擎。在 V8 中,所有的 JavaScript 对象都被表示为 "handles"(句柄)。Handle Scopes 是 V8 提供的一种管理内存的机制,它可以确保对象(handles)不会泄漏,并且在合适的时候得到清理。

那么 napi_escapable_handle_scope 是什么?napi_escapable_handle_scope 是一个特殊类型的 handle scope,它允许一个从其中创建的句柄“逃逸”出来,变成可以在父级作用域中使用的句柄。

在 N-API 中,通常我们会创建一个 napi_handle_scope 来管理我们创建的句柄,这样可以避免内存泄露。而当我们想要将某个句柄传递给另外一个作用域使用时,普通的 napi_handle_scope 是做不到的,这时我们就需要用到 napi_escapable_handle_scope

实际运用例子: 让我们假设你正在编写一个原生模块,该模块需要创建一个新的 JavaScript 对象并返回给调用者。

##include `<`node_api.h>

napi_value CreateObject(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_escapable_handle_scope scope;
    napi_value object;

    // 创建一个 escapable handle scope
    status = napi_open_escapable_handle_scope(env, &scope);
    if (status != napi_ok) return nullptr;

    // 创建一个新的 JavaScript 对象
    status = napi_create_object(env, &object);
    if (status != napi_ok) {
        napi_close_escapable_handle_scope(env, scope);
        return nullptr;
    }

    // 这里可以设置对象的属性等

    // "逃逸"这个对象出去,使它能在函数外部使用
    napi_value result;
    status = napi_escape_handle(env, scope, object, &result);
    if (status != napi_ok) {
        napi_close_escapable_handle_scope(env, scope);
        return nullptr;
    }

    // 关闭 handle scope
    status = napi_close_escapable_handle_scope(env, scope);
    if (status != napi_ok) return nullptr;

    return result; // 返回逃逸后的对象
}

在这个例子中,我们首先打开了一个 escapable handle scope,这允许我们稍后通过 napi_escape_handle 函数将创建的 JavaScript 对象传递到这个作用域之外。然后我们创建了一个新的对象,并通过 napi_escape_handle "逃逸" 这个对象。最后,我们关闭了 handle scope 并返回了逃逸的对象。

这就是 napi_escapable_handle_scope 的基本用法,它在编写涉及跨作用域传递 JavaScript 对象的原生模块时非常有用。

napi_refopen in new window

好的,首先我会简单介绍一下 Node.js 和 N-API 是什么,然后再详细解释 napi_ref,并给出实际的例子。

Node.js 简介: Node.js 是一个用于构建服务器端和网络应用程序的开源跨平台 JavaScript 运行时环境。它允许你使用 JavaScript 编写服务器端代码,这是一种通常仅用于编写网页前端代码的语言。

N-API 简介: N-API 是 Node.js 提供的一个 C API,它旨在减少 Node.js 原生插件与不同版本的 V8 引擎(Node.js 的底层 JavaScript 引擎)之间的兼容性问题。简而言之,N-API 让开发者可以编写只需要编译一次就能在多个版本的 Node.js 上运行的原生插件。

napi_ref 是什么napi_ref 是 N-API 中的一个数据类型,代表一个对 JavaScript 对象的引用。在 Node.js 的原生插件中,当你想要保持对某个 JavaScript 对象的引用,以便在将来的本地函数调用中使用它时,你会用到 napi_ref

如果没有 napi_ref,JavaScript 对象可能会因为垃圾回收而被销毁,特别是当对象不再被任何变量引用时。通过创建一个 napi_ref,你可以确保该对象在本地代码中仍然有效,直到你主动删除该引用。

实际的例子

假设你正在编写一个 Node.js 原生插件,该插件需要在本地代码中保存一个 JavaScript 函数,以便稍后调用它。你可以使用 napi_create_reference 创建一个 napi_ref 来存储对该函数的引用。

以下是如何在 N-API 中创建和使用 napi_ref 的简单示例:

##include `<`node_api.h>

// 定义一个全局变量来存储引用
static napi_ref myFunctionRef;

// 一个初始化函数,它接受一个 JavaScript 函数并保存它的引用
napi_value Init(napi_env env, napi_value exports) {
  // 获取传入的参数(我们假定是一个函数)
  napi_value myFunction;
  napi_get_cb_info(env, args, &myFunction, NULL, NULL, NULL);

  // 创建一个引用
  napi_create_reference(env, myFunction, 1, &myFunctionRef);

  return exports;
}

// 一个调用保存的 JavaScript 函数的函数
void CallMyFunction(napi_env env) {
  napi_value myFunction;
  napi_get_reference_value(env, myFunctionRef, &myFunction);

  // 调用该函数
  napi_value global;
  napi_get_global(env, &global);
  napi_call_function(env, global, myFunction, 0, NULL, NULL);
}

// 删除引用
void DeleteReference(napi_env env) {
  napi_delete_reference(env, myFunctionRef);
}

在上面的示例中,我们定义了一个名为 Init 的初始化函数,它创建了一个引用来保存 JavaScript 环境中传递进来的函数。然后,在 CallMyFunction 函数中,我们通过这个引用获取函数对象并调用它。最后,我们可以使用 DeleteReference 函数来删除这个引用,以允许 JavaScript 对象被垃圾回收。

请注意,这里的例子非常简单,实际应用中需要更多的错误检查和资源管理机制。

napi_type_tagopen in new window

N-API 是 Node.js 的一个 API,它提供了一个抽象层,使得你可以在不同版本的 Node.js 运行时中编写原生插件,而不用担心底层的变化。napi_type_tag是 N-API 中的一个功能,旨在帮助你确保你在 JavaScript 和 C 之间传递的对象是正确的类型。

要理解napi_type_tag的作用,我们先得知道类型标记(type tagging)是什么。类型标记是一种技术,用于标识特定的对象,以便能够验证对象是否为预期的类型。当你在 C 和 JavaScript 之间传递对象时,有时你需要确保接收到的对象正是你发送的对象的类型,这样才能安全地进行操作。

使用napi_type_tag,你可以给一个自定义的对象指定一个独一无二的"标签"(即napi_type_tag结构)。然后,每次该对象通过 N-API 接口从 C 传到 JavaScript 或从 JavaScript 传回 C 时,N-API 都会检查这个标签确保它匹配,如果不匹配,就会报错。

举例说明:

假设你正在编写一个 Node.js 原生模块,该模块提供了一个名为“MyObject”的自定义对象,并且你想确保只有“MyObject”实例可以被用于某些特定的函数调用。

  1. 首先,你会创建一个全局的napi_type_tag变量来代表"MyObject"类型:
napi_type_tag my_object_type_tag = { 0 }; // 初始化一个空的type tag
  1. 当你在 C 代码里创建一个新的"MyObject"实例时,你会使用napi_type_tag_instance()函数来将这个新对象与my_object_type_tag关联起来:
napi_value create_my_object(napi_env env) {
    napi_value my_object;
    // ... 创建my_object的代码 ...
    napi_status status = napi_type_tag_instance(env, my_object, &my_object_type_tag);
    // 检查并处理status是否成功...
    return my_object;
}
  1. 如果你有一个函数,它的输入参数应该是"MyObject"类型的对象,你可以在这个函数中使用napi_check_object_type_tag()来验证传入的对象是否真的带有正确的标签:
napi_value use_my_object(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value this_arg;
    void* data;

    // 从info中获取JavaScript传入的参数
    napi_get_cb_info(env, info, &argc, args, &this_arg, &data);

    // 检查传入的对象是否是MyObject类型
    bool is_my_object;
    napi_status status = napi_check_object_type_tag(env, args[0], &my_object_type_tag, &is_my_object);
    // 检查并处理status是否成功...

    if (!is_my_object) {
        // 抛出错误,因为传入的不是MyObject类型
        napi_throw_error(env, NULL, "Argument is not a MyObject type.");
        return NULL;
    }

    // 在这里执行其他操作...

    return some_result;
}

通过这种方式,你就可以确保在原生模块中的函数只接收和操作正确类型的对象,从而避免潜在的类型混淆和运行时错误。这对于在原生模块中维护类型安全非常重要,尤其是当涉及到内存管理时,错误的类型操作可能导致程序崩溃或者安全漏洞。

napi_async_cleanup_hook_handleopen in new window

napi_async_cleanup_hook_handle 是 Node.js 中的一个特性,它属于 N-API(Node API),这是一个用于构建原生插件的 API。在理解 napi_async_cleanup_hook_handle 之前,需要了解几个基本概念:

  1. N-API:这是一个 C 语言级别的 API,让开发者可以不依赖于 V8 引擎直接与 Node.js 进行交互。它的好处是使得原生插件不受特定版本的 Node.js 限制,增加了跨版本兼容性。

  2. 原生插件:通常用 C 或 C++编写,它们可以直接使用操作系统底层能力或其他本地库,为 Node.js 提供额外功能。

  3. 异步清理钩子:在 Node.js 中,当你创建异步资源时(例如,开始一个新的操作系统线程或者分配一些需要在未来某个时间点释放的内存),可能需要在这些资源不再需要时进行清理。异步清理钩子就是在 Node.js 环境即将关闭时确保这些资源被正确清理的机制。

现在,讲解一下 napi_async_cleanup_hook_handle

  • 当你使用 N-API 创建原生插件,并在其中创建了异步资源时,你可能希望在 Node.js 退出前执行一些清理工作。
  • napi_async_cleanup_hook_handle 就是这样一个机制。具体来说,它是一种类型,代表了一个清理钩子的“句柄”(handle)。
  • 你可以通过调用 napi_add_async_cleanup_hook 函数来注册一个清理函数,这个函数会在适当的时候被调用,以便进行资源清理。
  • 注册清理函数时,你会获得一个 napi_async_cleanup_hook_handle 实例,你可以使用它来移除注册的钩子(如果在某个时间点你决定不再需要它)。

实际应用示例: 假设你正在编写一个原生插件,该插件需要在背后运行一个计时器。当 Node.js 进程结束时,你需要确保这个计时器被停止并且相关资源被释放。你可以这样做:

##include `<`node_api.h>

void cleanup_timer(void* arg) {
  // 假设arg是指向你的计时器资源的指针
  // 在这里写上停止计时器和释放资源的代码
}

napi_value Init(napi_env env, napi_value exports) {
  // ... 初始化代码,比如创建计时器等

  // 注册清理钩子
  napi_async_cleanup_hook_handle handle;
  napi_status status = napi_add_async_cleanup_hook(env, cleanup_timer, /*arg=*/timer, &handle);

  if (status != napi_ok) {
    // 处理错误情况
  }

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中,我们定义了一个 cleanup_timer 函数,当 Node.js 进程退出时,这个函数会被调用来清理计时器。然后,在模块的初始化函数 Init 中,我们使用 napi_add_async_cleanup_hook 来注册这个清理函数,并传递计时器作为参数。

总结起来,napi_async_cleanup_hook_handle 是一种确保原生插件的异步资源在 Node.js 环境关闭前得到合适处理的机制。通过注册清理钩子,可以帮助开发者管理资源的生命周期,避免内存泄漏等问题。

Node-API callback typesopen in new window

Node-API(之前称为 N-API)是 Node.js 的一个 API 层,它允许你编写能够在不同版本的 Node.js 上运行的本机插件。本机插件是使用 C 或 C++编写的模块,它们可以直接与 Node.js 的 V8 引擎交互,这使得它们在执行速度上非常高效。但是,这些代码通常需要针对不同版本的 Node.js 进行修改,因为内部的 V8 API 可能会有变化。Node-API 旨在提供一个稳定、版本无关的 API,使得本机插件开发者不必担心这些问题。

在 Node-API 中,"回调类型"指的是当你创建一个原生函数时,可以传递给 Node-API 的不同种类的函数签名。这些回调类型定义了你的函数应该如何与 JavaScript 代码互动。下面分别解释一下这些类型,并提供一些实际的例子。

  1. napi_callback: 这是最基本的回调类型。它表示一个本机函数,该函数可以被 JavaScript 代码调用。它通常用于执行某些操作,然后返回结果给 JavaScript。

    例子: 假设我们想要创建一个本机函数,它接收两个数值参数,返回它们的和。我们会定义一个napi_callback函数,像这样:

    napi_value Add(napi_env env, napi_callback_info info) {
        napi_status status;
        size_t argc = 2;
        napi_value argv[2];
        status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
    
        // 检查传入参数的数量和类型等...
    
        double value1, value2;
        status = napi_get_value_double(env, argv[0], &value1);
        status = napi_get_value_double(env, argv[1], &value2);
    
        napi_value sum;
        status = napi_create_double(env, value1 + value2, &sum);
    
        return sum;
    }
    

    在 JavaScript 中,你可以像调用普通函数那样调用这个Add函数。

  2. napi_threadsafe_function: 当你需要从任何线程安全地调用 JavaScript 函数时,就会用到这个类型。例如,如果你创建了一个工作线程,在工作完成后想要通知 JavaScript 主线程,则可以使用这个类型。

    例子: 假设我们有一个执行耗时计算的工作线程,我们想在完成后将结果回传给 JavaScript。你可以创建一个napi_threadsafe_function并在工作线程中调用它。

    void CallJs(napi_env env, napi_value js_callback, void* context, void* data) {
        // 从数据中获取计算的结果,并通过回调函数传递给JavaScript
    }
    
    // 其他代码会设置工作线程,并在适当的时候调用CallJs
    

    然后在 JavaScript 中,你可以提供一个回调函数来接收和处理结果。

  3. napi_async_execute_callbacknapi_async_complete_callback: 这两个类型用于异步操作。第一个用于在独立的线程中执行长时间运行的任务,而第二个用于在任务完成时在主事件循环中执行回调。

    例子: 如果我们想读取一个大文件而不阻塞主事件循环,我们可以创建一个异步操作。使用这两个回调,我们可以在单独的线程中读取文件内容(napi_async_execute_callback),然后在读取完成时通知 JavaScript 主线程(napi_async_complete_callback)。

    void ExecuteReadFile(napi_env env, void* data) {
        // 在此处执行文件读取操作
    }
    
    void CompleteReadFile(napi_env env, napi_status status, void* data) {
        // 处理完成后的回调,例如将读取的内容传递给JavaScript
    }
    
    // 使用napi_create_async_work创建异步工作,然后使用napi_queue_async_work将其加入事件循环
    

通过使用这些回调类型,你可以将复杂的、耗时的或需要跨线程操作的任务从 JavaScript 移至更底层的 C/C++代码中处理,同时保证 JavaScript 侧的事件循环不会被阻塞。这样做的好处是可以极大地提升性能,特别是在 CPU 密集型或 IO 密集型的场景下。

napi_callback_infoopen in new window

Node.js 的 N-API 是一个用于构建原生插件的 API。原生插件是用 C 或 C++ 编写的模块,可以被 Node.js 直接调用。napi_callback_info 是 N-API 中的一个概念,它代表了一次从 JavaScript 到原生函数的调用的上下文信息。

当你在 JavaScript 中调用一个原生模块里的函数时,这个函数实际上是用 C 或 C++ 实现的。JavaScript 代码和原生代码之间需要一个“桥梁”来传递信息。这个“桥梁”就是由 napi_callback_info 提供的。

示例理解 napi_callback_info

假设我们有一个简单的 Node.js 原生模块,它提供了一个函数 sayHello,当你从 JavaScript 调用这个函数时,它将返回 "Hello, World!" 字符串。

JavaScript 调用:

const nativeModule = require("my-native-module");
console.log(nativeModule.sayHello()); // 输出: Hello, World!

在原生模块中,使用 C 语言实现 sayHello 函数可能如下所示:

##include `<`node_api.h>

napi_value SayHello(napi_env env, napi_callback_info info) {
    napi_value greeting;
    napi_status status;

    // 创建一个字符串 'Hello, World!' 返回给 JavaScript
    status = napi_create_string_utf8(env, "Hello, World!", NAPI_AUTO_LENGTH, &greeting);
    if (status != napi_ok) {
        // 处理错误...
    }

    return greeting;
}

// 初始化函数,注册 `sayHello`
NAPI_MODULE_INIT() {
    napi_value sayHelloFn;
    napi_status status;

    status = napi_create_function(env, NULL, 0, SayHello, NULL, &sayHelloFn);
    if (status != napi_ok) {
        // 处理错误...
    }

    status = napi_set_named_property(env, exports, "sayHello", sayHelloFn);
    if (status != napi_ok) {
        // 处理错误...
    }

    return exports;
}

在这个例子中,SayHello 函数是我们要给 JavaScript 调用的原生函数。这个函数接受两个参数:envinfo

  • env 是一个表示当前执行环境的变量。
  • info 就是我们讨论的 napi_callback_info。它包含了调用这个函数时所有重要的上下文信息,比如调用的参数、this 的值等等。

SayHello 函数内部,我们不直接处理 JavaScript 传入的参数,而是通过 napi_callback_info 来获取这些信息。例如,如果我们想知道调用时传递了多少个参数,我们可以使用 napi_get_cb_info 函数(这是 N-API 提供的函数之一)来从 info 中获取这些信息。

实际应用中,napi_callback_info 更多地被用于获取参数、处理不同数量的参数、获取 this 对象引用等高级操作。但基本思路是将 JavaScript 中的调用信息映射到 C/C++ 的世界中,然后在原生代码层面处理这些信息。

总结起来,napi_callback_info 是原生模块中非常重要的一个组件,因为它使得 JavaScript 和 C/C++ 之间的交互成为可能,并且管理着这种交互过程中所有必要的上下文信息。

napi_callbackopen in new window

Node.js 中的 N-API 是一个为了构建原生插件(也就是用类似 C 或 C++这样的低级语言编写的模块)而设计的稳定的 API 层。它的目的是让你能够不受 Node.js 版本更迭的影响,编写能够在多个版本上运行的插件。

napi_callback 是这个 N-API 的一部分,它是一个函数指针类型,你在创建原生插件时会使用这种类型的变量来引用你的 C 或 C++ 代码中的函数。当 JavaScript 代码调用这些原生函数时,底层的 Node.js 引擎会通过这个 napi_callback 指向的函数来执行相应的 C/C++ 代码。

通俗地说,napi_callback 就像是一个桥梁,连接了 JavaScript 的世界和 C/C++ 的世界。你可以把它想象成一个电话号码,JavaScript 代码通过这个号码“打电话”给 C/C++ 函数,然后那边的代码接听并处理请求,最终将结果返回给 JavaScript。

实际运用例子:

假设你想要在 Node.js 中使用某个 C 库中的功能,例如处理图像的库。这个库提供了一个函数叫做 process_image,你想通过 Node.js 调用这个函数。

首先,你需要在 C 代码中定义一个符合 napi_callback 类型签名的函数,我们可以叫它 ProcessImageWrapper

##include `<`node_api.h>

// 假设这是那个图像处理库提供的函数
void process_image(const char* image_path);

// 这是你定义的 napi_callback 对应的函数
napi_value ProcessImageWrapper(napi_env env, napi_callback_info info) {
    // 从 JavaScript 获取参数,调用 process_image 函数等逻辑
    // ...

    // 返回处理结果给 JavaScript
    // ...
}

之后,你需要注册这个函数到 Node.js 环境中,让 JavaScript 能够找到并调用它:

napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;

    // 创建一个新的函数对象ProcessImageWrapper,暴露给JavaScript
    napi_create_function(env, NULL, 0, ProcessImageWrapper, NULL, &fn);

    // 设置函数名称为 'processImage',并把它作为导出的模块的属性
    napi_set_named_property(env, exports, "processImage", fn);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

现在,在 JavaScript 代码中,你可以这样调用这个函数:

const addon = require("./build/Release/addon");

// 假设这里 './image.png' 是你想要处理的图片路径
addon.processImage("./image.png", (error, result) => {
  if (error) {
    console.error("Image processing failed:", error);
  } else {
    console.log("Image processed successfully:", result);
  }
});

在这个例子中,JavaScript 代码通过 napi_callback 指定的 ProcessImageWrapper 函数调用了 C 代码,从而实现了与原生图像处理库的交互。这只是一个简化示例,实际的错误处理和数据转换会更复杂,但它展示了如何使用 napi_callback 在 Node.js 中创建和使用原生扩展的基本概念。

node_api_nogc_finalizeopen in new window

node_api_nogc_finalize 是 Node.js 中的一个 N-API 函数,它允许你设置一个对象在被垃圾回收(GC)之前不会调用其终结器(finalizer)。这个函数主要用于性能优化。

在 Node.js 中,N-API 提供了一套用于构建原生扩展的 C API。当使用 N-API 创建原生资源时(例如在 C/C++ 模块中分配内存),通常需要指定当 JavaScript 对象被垃圾回收时应该如何清理这些资源。这是通过为该对象设置一个终结器来实现的。

然而,在某些情况下,我们完全知道该资源将在某个特定时间点之前不需要被清理,或者希望手动管理资源的生命周期,以避免与垃圾回收器的潜在性能开销。在这种情况下,可以使用 node_api_nogc_finalize 函数来告诉 N-API 不要在垃圾回收时调用对象的终结器。

以下是一个使用 node_api_nogc_finalize 的简单例子:

##include `<`node_api.h>

// 假设这是我们的一些原生资源
typedef struct {
  int some_resource;
} MyResource;

// 这是正常的清理函数,如果设置了,通常会在对象被垃圾回收时调用
void Finalize(napi_env env, void* finalize_data, void* finalize_hint) {
  MyResource* my_resource = (MyResource*)finalize_data;
  // 在这里释放资源
  free(my_resource);
}

// 这个函数可以在适当的时候手动调用以清理资源
void ManualCleanup(napi_env env, napi_value exports) {
  // 获取之前关联到 JavaScript 对象的原生资源
  MyResource* my_resource;
  napi_get_instance_data(env, &my_resource);

  // 清理资源
  free(my_resource);

  // 设置 instance data 为 NULL,避免重复清理
  napi_set_instance_data(env, NULL, NULL, NULL);
}

// 初始化函数,创建和设置实例数据
napi_value Init(napi_env env, napi_value exports) {
  MyResource* my_resource = malloc(sizeof(MyResource));
  my_resource->some_resource = 42; // 例子中的假资源

  // 将资源与 JavaScript 对象关联,并告诉 N-API 不要在垃圾回收时调用终结器
  napi_set_instance_data(env, my_resource, NULL /* 使用NULL代替Finalize */, NULL);

  // ... 这里可能会有更多代码 ...

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的例子中,Init 函数用于初始化一个原生模块,并且使用 napi_set_instance_data 来关联一个原生资源,但是我们传递了 NULL 而不是 Finalize 函数。这样,当垃圾回收器尝试清理相关的 JavaScript 对象时,它不会尝试调用终结器函数来清理 MyResource。因此,必须确保在合适的时候手动调用 ManualCleanup 函数来清理资源,否则就会发生内存泄漏。

请注意,以上代码仅为说明使用 node_api_nogc_finalize 的场景,并非一个可直接运行的例子。在实际项目中使用时,你需要根据你的具体需求和资源类型进行适当的修改。

napi_finalizeopen in new window

napi_finalize 是 Node.js 中 N-API(原生 API)的一部分。N-API 是 Node.js 提供给 C 和 C++ 插件开发者的一个稳定的 API 集合,它允许开发者编写可以与不同版本的 Node.js 兼容的本地插件。

在详细解释 napi_finalize 之前,需要知道几个背景信息:

  1. 垃圾回收(Garbage Collection,GC):JavaScript 在运行过程中会创建和使用很多对象。当这些对象不再被需要时,JavaScript 的垃圾回收器会自动释放这些对象所占用的内存。但是,如果你在本地插件中创建了一些非 JavaScript 资源(比如在 C/C++ 中分配的内存、打开的文件描述符或者其他系统资源),JavaScript 的垃圾回收器并不知道如何处理这些资源。

  2. 引用计数(Reference Counting):这是一种确保资源正确管理的技术。每当你创建一个对象,并把它赋值给某个变量,这个对象的引用计数就会增加。相反,当这个对象的引用减少到零时,意味着没有任何变量是指向这个对象的,这时候资源就可以被清理了。

现在,让我们深入了解 napi_finalize

napi_finalize 是一个回调函数,你作为插件开发者在创建一个能够通过 JavaScript 访问的本地资源时提供给 N-API 的。这个回调函数的目的是在相关 JavaScript 对象被垃圾回收器回收时,你可以有机会去清理那些由本地代码创建的非 JavaScript 资源。

当你通过 N-API 函数 napi_wrap 将一个本地资源与一个新的或现有的 JavaScript 对象关联时,你可以指定一个 napi_finalize 回调函数。这样,当这个 JavaScript 对象被垃圾回收时,napi_finalize 所指定的函数就会被调用,这时你可以在这个函数内部执行必要的清理工作。

举个例子来说:

假设你正在编写一个 Node.js 本地插件来读取操作系统的系统信息。你可能会在 C++ 中创建一个类 SystemInfo 来处理与系统交互的具体细节,并且想要在 JavaScript 中也能够使用这个类。在这种情况下,你会:

  1. 使用 napi_create_object 创建一个新的 JS 对象。
  2. 使用 napi_wrap 将这个 JS 对象与一个 SystemInfo 类的实例关联起来。
  3. 当 JS 对象即将被垃圾回收时,你需要清理 SystemInfo 实例所占用的资源,比如关闭文件句柄等。这就是你需要提供一个 napi_finalize 回调函数的地方。

示例代码片段:

##include `<`node_api.h>

// 假设这是你的系统信息类
class SystemInfo {
public:
    SystemInfo() {
        // 初始化代码
    }

    ~SystemInfo() {
        // 清理代码,例如关闭文件句柄等
    }

    // 其他方法...
};

// 这是 napi_finalize 回调函数
void FinalizeSystemInfo(napi_env env, void* finalize_data, void* finalize_hint) {
    // 在这里释放 SystemInfo 实例
    SystemInfo* sys_info = reinterpret_cast`<`SystemInfo*>(finalize_data);
    delete sys_info;
}

// 这是 N-API 暴露给 JS 的方法,用于创建 SystemInfo 对象
napi_value CreateSystemInfoWrapper(napi_env env, napi_callback_info info) {
    napi_status status;

    napi_value sys_info_obj;
    status = napi_create_object(env, &sys_info_obj);

    SystemInfo* sys_info = new SystemInfo(); // 创建 SystemInfo 实例

    // 把 SystemInfo 实例和 JS 对象关联起来,并指定 FinalizeSystemInfo 作为清理回调
    status = napi_wrap(env, sys_info_obj, sys_info, FinalizeSystemInfo, nullptr, nullptr);

    return sys_info_obj; // 返回新创建的 JS 对象
}

在上面的代码中,我们定义了一个 FinalizeSystemInfo 函数作为 napi_finalize 回调,当相关联的 JavaScript 对象即将被 GC 清理时,该函数会被调用以释放 SystemInfo 实例。这样我们就确保了,无论何时 JavaScript 对象被回收,其对应的本地资源都能够得到适当的处理,避免了内存泄露。

napi_async_execute_callbackopen in new window

Node.js 的 N-API(原生 API)是一个让你能够用 C 或者 C++ 编写扩张模块的界面。当你想要提高性能,或者使用一些 Node.js 中没有直接提供的本地库时,你可能会用到它。

napi_async_execute_callback 是 N-API 中的一个函数,它允许你在创建异步任务时定义要执行的逻辑。当你调用这个函数,Node.js 会在后台线程上执行定义的回调函数,而不会阻塞主事件循环。这对于不希望影响到主线程性能的密集型计算任务非常有用。

为了理解 napi_async_execute_callback,我们需要先明白几个概念:

  • 异步编程:在 Node.js 中,异步编程指的是程序在等待某个操作完成时可以继续执行其他代码,比如读取文件、数据库操作或者网络请求。
  • 事件循环:Node.js 使用事件循环来处理异步操作。当所有同步代码执行完毕后,它会检查是否有异步任务完成并准备好回调函数执行。
  • 多线程:虽然 JavaScript 在 Node.js 中默认是单线程运行的,但 Node.js 可以利用 C++ 扩展或者工作线程(worker threads)在后台执行多线程任务。

现在,让我们举个例子来描述 napi_async_execute_callback 是如何工作的:

假设你有一个耗时的图像处理任务,在 Node.js 中你想通过一个扩展模块来实现,以避免阻塞主事件循环。首先,你会用 C++ 来写这个图像处理逻辑,然后需要向 Node.js 注册这个任务,告诉它这是一个异步任务。

  1. 你会创建一个 napi_async_work 对象,它代表了一个将要被异步执行的任务。
  2. 你会定义一个 Execute 回调,这个回调包含了真正的处理逻辑,即图像处理算法。
  3. 你将这个 Execute 回调与 napi_async_work 对象关联,并通过调用 napi_async_execute_callback 来在一个后台线程上执行它。

这个过程允许 Node.js 在处理该任务的同时,继续处理其他事件循环中的事项,如处理 HTTP 请求或者用户的输入。

以下是一个抽象的伪代码示例,展示了如何使用 napi_async_execute_callback

##include `<`node_api.h>

// 这个函数是耗时任务的实际逻辑
void Execute(napi_env env, void* data) {
    // 执行一些耗时的操作,例如图像处理
}

// 这个函数会在耗时任务完成后被调用,通常用来返回结果给JavaScript
void Complete(napi_env env, napi_status status, void* data) {
    // 处理完成后,返回结果给 JavaScript 环境
}

// 这个函数是用来启动整个异步任务的
napi_value StartAsyncWork(napi_env env, napi_callback_info info) {
    napi_value work_name;
    napi_create_string_utf8(env, "WorkName", NAPI_AUTO_LENGTH, &work_name);

    // 创建 async work
    napi_async_work work;
    napi_create_async_work(env, NULL, work_name, Execute, Complete, NULL, &work);

    // 将这个 work 排入队列,使其开始在后台线程上执行
    napi_queue_async_work(env, work);

    return NULL;
}

以上代码是非常简化的示例,仅用于说明 napi_async_execute_callback 的基本概念。在实际应用中,你还需要管理内存,处理错误,并确保跨语言边界(C++ 到 JavaScript)时数据正确传递。

napi_async_complete_callbackopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,让你可以在服务器端执行 JavaScript 代码。N-API 是 Node.js 提供的一套 C API,使得原生插件(native addons)能够不受 Node.js 版本影响地编译和运行。

在 Node.js 的 N-API 中,napi_async_complete_callback 是一个类型定义,它是用于异步操作的回调函数的签名。这个回调函数在一个异步操作完成时被调用。

为了理解 napi_async_complete_callback,需要先明白几个概念:

  1. 原生插件:这些插件是使用 C 或 C++ 编写的,通过 N-API 可以与 Node.js 交互。
  2. 异步操作:在 Node.js 中,很多操作都是异步进行的,即它们会在后台执行,并在完成时通知 Node.js 主线程。
  3. 回调函数:当异步操作完成时,用来接收结果或者错误信息的函数。

现在,让我们具体看一下 napi_async_complete_callback。这个回调函数定义如下:

typedef void (*napi_async_complete_callback)(napi_env env,
                                              napi_status status,
                                              void* data);
  • napi_env env: 这个参数是一个表示 N-API 环境的句柄,它提供了大量的 API 函数使得你能在原生插件中操作 JavaScript 代码、对象等。
  • napi_status status: 这个参数表示异步操作的完成状态,它可以告诉你操作是否成功,或者发生了哪种类型的错误。
  • void* data: 这是一个指向任意数据的指针,你可以在开始异步操作时传递一个指向你的数据的指针,在回调函数被调用时再从这个指针获取数据。

假设你正在编写一个原生插件,其中有一个需要执行长时间计算的异步操作。下面是一个使用 napi_async_complete_callback 的例子:

// 假设这个函数是一个长时间运行的异步操作
void long_running_operation(void* data) {
    // 执行一些长时间的操作...
}

// 这是当上述长时间运行的操作完成后会被调用的回调函数
void operation_completed(napi_env env, napi_status status, void* data) {
    // 根据 status 参数检查操作是否成功完成
    if (status == napi_ok) {
        // 如果成功,处理结果
    } else {
        // 如果失败,处理错误
    }
}

// 在你的插件中启动异步操作,并提供上面的回调
void start_async_work() {
    // 初始化一些数据
    void* data = ...;

    // 启动异步操作
    long_running_operation(data);

    // 设置当操作完成时要调用的回调函数
    napi_async_complete_callback callback = operation_completed;

    // 更多的代码来安排回调函数在合适的时机被调用...
}

注意:实际上你不能直接这样简单地调用 long_running_operationoperation_completed,因为这将同步执行并不是异步。在 Node.js 的 N-API 中,你必须使用正确的模式比如 napi_create_async_worknapi_queue_async_work 来安排异步工作和相应的完成回调。以上代码只是为了演示 napi_async_complete_callback 的概念而已。

总结一下,napi_async_complete_callback 是定义异步操作完成时调用的回调函数类型,它允许原生插件开发者处理异步操作的结果或错误。

napi_threadsafe_function_call_jsopen in new window

Node.js 中的 napi_threadsafe_function_call_js 是一个用于多线程编程的高级 API 函数,它允许你在任何线程中安全地调用 JavaScript 函数。这很重要,因为通常在 Node.js 中,只能在主线程(也就是事件循环运行的线程)中直接执行 JavaScript。

napi_threadsafe_function_call_js 属于 N-API,这是 Node.js 提供的一组 C 语言 API。通过 N-API,原生插件可以不受 Node.js 版本更进建立和维护,同时保持对前后版本的兼容性。

概念

在多线程程序中,当你想从一个非主线程(例如,一个由 C++库创建的线程)调用 JavaScript 函数时,如果直接进行这样的调用,很可能会导致竞争条件、内存泄露或程序崩溃等问题,因为 V8 引擎(Node.js 的 JavaScript 引擎)并不是线程安全的。

为了解决这个问题,napi_create_threadsafe_function() 函数被用来创建一个所谓的“线程安全函数”。然后,你可以从任何线程使用 napi_threadsafe_function_call_js 来调用这个 JavaScript 函数,而不用担心上述那些问题。

使用例子

假设我们有一个耗时的运算任务,我们想要在一个 C++的辅助线程中执行它,但在任务完成后我们需要回到 Node.js 的主线程来处理结果。

首先,我们需要创建一个线程安全函数:

##include `<`node_api.h>

// 这是将被辅助线程调用的JavaScript函数
void js_callback(napi_env env, napi_value js_callback, void* context, void* data) {
    // 在这里构造你的回调逻辑
}

...

// 调用napi_create_threadsafe_function创建一个线程安全函数
napi_value resource_id;
napi_create_string_utf8(env, "MyResource", NAPI_AUTO_LENGTH, &resource_id);
napi_threadsafe_function tsfn;
napi_create_threadsafe_function(env,
                                js_func,      // 传入的JS函数
                                NULL,         // async_resource
                                resource_id,  // async_resource_name
                                0,            // max_queue_size
                                1,            // initial_thread_count
                                NULL,         // context
                                NULL,         // finalize_cb
                                NULL,         // finalize_hint
                                js_callback,  // native回调
                                &tsfn);
...

然后,在一个辅助线程中,当我们想要调用 JavaScript 函数时:

void some_async_work(void* data) {
    ...
    // 执行某些异步操作
    ...

    // 当准备好调用JavaScript时:
    napi_call_threadsafe_function(tsfn, data, napi_tsfn_blocking); // data 是将传递给 `js_callback` 的参数

    ...
}

...

// 创建新线程并开始异步工作
pthread_t thread_id;
pthread_create(&thread_id, NULL, some_async_work, NULL);

...

最后,当我们不再需要线程安全函数时,释放它:

napi_release_threadsafe_function(tsfn, napi_tsfn_release);

上面的代码示例说明了如何设置一个线程安全函数并在辅助线程中调用它。这里没有展示完整的代码,因为实际的实现需要考虑 Node.js 模块的初始化、错误处理等多个方面。但基本思路就是创建一个可以在线程之间共享并安全调用的 JavaScript 函数,并且确保当你完成使用时正确地清理资源。

napi_cleanup_hookopen in new window

Node.js 的 N-API 是一个用于构建原生插件的接口。原生插件是使用 C, C++等编程语言编写的模块,可以被 Node.js 直接调用,通常用于性能关键型任务或者调用操作系统级别功能。

napi_cleanup_hook 是 N-API 中的一个功能,它允许原生插件在 Node.js 的环境被清理前执行一些特定的代码。换句话说,当你的 Node.js 应用程序准备关闭时(例如调用了process.exit()或者正常结束),这个钩子函数就会被触发。

这很有用,因为它为原生模块提供了一种在垃圾收集器销毁之前自动释放资源的机制。如果你创建了一些需要手动清理的资源(比如动态分配的内存、文件描述符、线程等),那么使用napi_cleanup_hook就非常重要了。

例子 1:假设您编写了一个原生模块,该模块打开一个日志文件,并且希望在 Node.js 退出时关闭该文件以防止数据损坏。

void cleanup_file(void* arg) {
    FILE* file = (FILE*)arg;
    fclose(file);
}

// ...在某处注册清理钩子
FILE* log_file = fopen("log.txt", "a");
napi_add_cleanup_hook(env, cleanup_file, log_file);

在这个例子中,我们首先定义了一个名为cleanup_file的函数来关闭文件。然后,我们打开日志文件,并用napi_add_cleanup_hook注册了这个函数作为清理钩子,并将文件指针作为参数传递给它。

例子 2:假设你的原生模块启动了一个后台线程来执行一些长时间运行的计算,并且需要在应用程序退出时安全地停止该线程。

void cleanup_thread(void* arg) {
    my_thread_data* data = (my_thread_data*)arg;
    // 设置一个标志或发送信号来告诉线程停止运行
    stop_thread(data->thread_id);
    // 等待线程实际结束
    join_thread(data->thread_id);
}

// ...在某处注册清理钩子
my_thread_data* thread_data = start_background_thread();
napi_add_cleanup_hook(env, cleanup_thread, thread_data);

在这个例子中,假设我们有一个函数start_background_thread来启动后台线程,并返回一个包含线程信息的结构体。我们定义了一个cleanup_thread函数来处理线程的停止过程,并通过napi_add_cleanup_hook注册了它作为一个清理钩子。

总结:napi_cleanup_hook是一个强大的工具,它确保了即使 Node.js 应用程序正在退出,原生模块也能够有序地清理自己的资源,避免内存泄漏和其他潜在问题。记住,这是高级功能,并且主要用于那些创建复杂原生扩展的开发者。

napi_async_cleanup_hookopen in new window

Node.js 中的 napi_async_cleanup_hook 是一个功能,它允许你在 Node.js 的异步环境中注册清理钩子(cleanup hooks)。这是为了确保当一个异步资源,比如定时器、文件流等,不再需要时能够适当地执行清理工作。

在 Node.js 应用程序中,有时你会创建一些长期运行的异步操作。例如,可能会启动一个定时器来周期性地执行任务,或者打开一个文件流进行读写。当这些异步操作结束或不再需要时,与它们相关联的资源必须被正确地释放,以避免内存泄漏和其他潜在问题。

napi_async_cleanup_hook 函数就是为了解决这个问题而设计的。通过使用这个函数,你可以注册一个回调函数,当 Node.js 进程准备退出时,这个回调函数将被调用,使得你有机会去清理那些仍然挂起的异步资源。

举个例子来说:

  1. 创建定时器示例: 假设你在 Node.js 应用程序中设置了一个间隔定时器(interval timer),每隔一段时间执行某项任务。在某些情况下,如果你的应用程序需要优雅地关闭,你可能需要清除这个定时器。
const { createHook, asyncResource } = require("async_hooks");

// 假定存在一个定时器变量
let intervalTimer;

function initIntervalTimer() {
  // 设置定时器
  intervalTimer = setInterval(() => {
    console.log("定时器运行中...");
  }, 1000);

  // 使用 napi_async_cleanup_hook 注册清理钩子
  createHook({
    init(asyncId, type, triggerAsyncId, resource) {
      // 当 Node.js 退出时调用的清理函数
      if (type === "TIMERWRAP") {
        napi_async_cleanup_hook(() => {
          clearInterval(intervalTimer);
          console.log("定时器已清除");
        });
      }
    },
  }).enable();
}

initIntervalTimer();

// 在某个时间点之后停止应用程序
setTimeout(() => {
  process.exit(0); // 触发清理钩子
}, 5000);

在这个例子中,我们首先定义了一个初始化定时器的函数 initIntervalTimer(),它会创建一个定时器,并注册一个 napi_async_cleanup_hook 来清理这个定时器。当 process.exit(0) 被调用时,它会触发清理钩子,从而清除定时器并输出日志表示定时器已被清除。

  1. 文件流清理示例: 如果你打开了一个文件流用于读取或写入数据,可能也希望在应用程序退出之前关闭这个流。
const { createReadStream } = require("fs");
const { createHook, asyncResource } = require("async_hooks");

// 打开一个文件流
const stream = createReadStream("path/to/file.txt");

createHook({
  init(asyncId, type, triggerAsyncId, resource) {
    if (type === "FSREQCALLBACK") {
      // 当 Node.js 退出时调用的清理函数
      napi_async_cleanup_hook(() => {
        stream.close(() => {
          console.log("文件流已关闭");
        });
      });
    }
  },
}).enable();

// 在某个时间点之后停止应用程序
setTimeout(() => {
  process.exit(0); // 触发清理钩子
}, 5000);

这个例子中的代码类似,我们打开一个文件流并使用 napi_async_cleanup_hook 来注册一个清理钩子,它将在应用程序退出前关闭这个文件流。

重要的是要注意,napi_async_cleanup_hook 是 N-API 的一部分,N-API 是一个 C API,可用于构建原生插件。在 JavaScript 层面,我们通常使用 Node.js 提供的高级抽象如 async_hooks 模块来处理异步资源的生命周期。

上述例子中简化了许多底层的细节,并没有直接使用 napi_async_cleanup_hook,因为在纯 JavaScript 环境中不直接使用 N-API 函数。不过,对于编写本地插件(native addon)的开发人员来说,了解如何通过 N-API 使用这些钩子是很重要的。

Error handlingopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API 层,它允许你使用 C 或 C++ 代码与 JavaScript 运行时交互,并在不同版本的 Node.js 之间提供二进制兼容性。错误处理是编程中非常重要的一个环节,因为它可以帮助你理解程序运行时可能遇到的问题并妥善地对这些问题进行处理。

在 Node.js 的 N-API 中,错误处理通常涉及以下几个方面:

  1. N-API 函数返回值:当你调用一个 N-API 函数时,它通常会返回一个称为 napi_status 的枚举值。这个返回值表示函数调用是否成功。如果函数执行成功,它通常返回 napi_ok。如果发生错误,它将返回一个不同的枚举值,来描述发生了什么类型的错误。

    例如:

    napi_status status;
    napi_value result;
    
    // 尝试调用 N-API 函数并检查其返回状态
    status = napi_do_something(env, &result);
    if (status != napi_ok) {
      // 错误处理逻辑
    }
    
  2. 错误信息:当 N-API 返回一个错误状态时,你可以使用一些特定的 N-API 函数来获取更多关于错误的信息。例如,你可以获取错误消息或触发错误的引擎异常。

    例如:

    const napi_extended_error_info* error_info;
    status = napi_get_last_error_info(env, &error_info);
    if (status == napi_ok) {
      const char* error_message = error_info->error_message;
      // 使用错误信息进行处理
    }
    
  3. 异常处理:JavaScript 中的异常可以通过 N-API 捕获和抛出。如果有一个 JavaScript 异常被抛出,你可以使用 napi_is_exception_pending 函数来检查,并使用 napi_get_and_clear_last_exception 来获取异常对象。

    例如:

    napi_value exception;
    bool has_exception;
    status = napi_is_exception_pending(env, &has_exception);
    if (status == napi_ok && has_exception) {
      status = napi_get_and_clear_last_exception(env, &exception);
      // 现在你可以处理异常了
    }
    
  4. 创建和抛出错误:如果你需要从你的原生模块中抛出一个错误,你可以使用如 napi_throw_errornapi_throw_type_errornapi_throw_range_error 等函数来创建并抛出错误。

    例如:

    status = napi_throw_error(env, NULL, "An error occurred");
    if (status != napi_ok) {
      // 抛出错误失败的处理逻辑
    }
    

实际应用例子:

假设你正在编写一个原生插件,该插件需要完成一个数学计算,并且只接受正整数作为输入。你可以使用 N-API 的错误处理功能来确保输入有效,并在出现错误时通知 JavaScript。

napi_status sum_positive_integers(napi_env env, napi_callback_info info) {
  size_t argc = 2;
  napi_value args[2];
  napi_value this_arg;
  int64_t number1, number2;
  napi_status status;

  // 获取参数
  status = napi_get_cb_info(env, info, &argc, args, &this_arg, NULL);
  if (status != napi_ok || argc `<` 2) {
    napi_throw_error(env, NULL, "Function requires 2 arguments.");
    return status;
  }

  // 确保参数是数字并且是正整数
  status = napi_get_value_int64(env, args[0], &number1);
  if (status != napi_ok || number1 `<` 0) {
    napi_throw_type_error(env, NULL, "First argument must be a positive integer.");
    return status;
  }

  status = napi_get_value_int64(env, args[1], &number2);
  if (status != napi_ok || number2 `<` 0) {
    napi_throw_type_error(env, NULL, "Second argument must be a positive integer.");
    return status;
  }

  // 计算和
  int64_t sum = number1 + number2;
  napi_value result;
  status = napi_create_int64(env, sum, &result);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "An error occurred while creating return value.");
    return status;
  }

  // 返回结果
  return result;
}

上述例子中,我们检查了函数是否收到了正确数量的参数,验证了这些参数是否符合预期(正整数),如果检测到任何错误,我们就使用 N-API 的错误抛出函数来返回一个适当的错误给 JavaScript 环境。这样,JavaScript 调用者就可以捕获这些错误并据此采取相应的措施。

Return valuesopen in new window

Node.js 中的 N-API(原生 API)允许 JavaScript 代码和 C/C++原生插件之间进行交互。当你使用 N-API 编写原生扩展时,你会经常需要处理函数返回值。在这个上下文中,“返回值”特指从一个 N-API 调用返回到 JavaScript 的值。

在 Node.js v21.7.1 的文档中,关于返回值的说明主要是告诉开发者如何正确地从 N-API 函数返回结果给 JavaScript 代码。大多数 N-API 函数会返回类型为 napi_status 的值。这个值表示函数执行是否成功,或者发生了什么样的错误。

这里有一些可能的返回值:

  • napi_ok: 表示没有错误发生,函数调用成功。
  • napi_invalid_arg: 表示提供了无效参数。
  • napi_object_expected: 表示预期一个对象但没有得到。
  • napi_string_expected: 表示预期一个字符串但没有得到。
  • ...以及其他更多的状态码。

现在来看几个实际的例子:

示例 1:创建一个新的 JavaScript 字符串

假设我们正在编写一个将 C 语言的字符串复制到 JavaScript 字符串的 N-API 函数。函数首先检查参数有效,然后执行操作,并返回状态。

##include `<`node_api.h>

// 这是我们的本地扩展函数
napi_status CreateNewString(napi_env env, napi_value* result) {
    // 创建一个C字符串
    const char* str = "Hello from native code!";

    // 使用N-API创建一个新的JS字符串
    return napi_create_string_utf8(env, str, NAPI_AUTO_LENGTH, result);
}

// 注册函数作为模块导出
NAPI_MODULE_INIT() {
  napi_value new_str;
  napi_status status = CreateNewString(env, &new_str);
  if (status != napi_ok) {
    // 处理错误...
  }
  return new_str;
}

在这个示例中,CreateNewString函数创建了一个新的 JavaScript 字符串,并通过result参数返回它。如果操作成功,napi_create_string_utf8会返回napi_ok

示例 2:从函数中返回数值

另一个常见用例是从原生函数返回一个数字给 JavaScript。

##include `<`node_api.h>

// 这是我们的本地扩展函数
napi_status CalculateSum(napi_env env, int a, int b, napi_value* result) {
    int sum = a + b;

    // 将计算结果转换为JavaScript数字
    return napi_create_int32(env, sum, result);
}

// 注册函数作为模块导出
NAPI_MODULE_INIT() {
  napi_value sum_result;
  napi_status status = CalculateSum(env, 3, 5, &sum_result);
  if (status != napi_ok) {
    // 处理错误...
  }
  return sum_result;
}

在这个例子中,CalculateSum接收两个整数,计算它们的和,并通过result返回这个和的 JavaScript 数值。如果成功,napi_create_int32会返回napi_ok

注意,在这些示例中,我们没有展示错误处理的完整代码。在实际的 N-API 模块中,你需要检查每个 N-API 调用的返回状态,并适当地处理错误情况,例如释放资源、打印错误信息、或者返回一个错误对象到 JavaScript。

总结就是,Node.js 文档中关于 N-API 的“返回值”部分主要强调了从 N-API 函数返回状态码的重要性。这些状态码对于调试和确保 N-API 函数正确地与 JavaScript 交互至关重要。

napi_get_last_error_infoopen in new window

当您在使用 Node.js 进行编程,特别是当您在使用 N-API(Node.js 的一个用于构建原生插件的 API)时,您可能会遇到错误和问题。这时候,napi_get_last_error_info 就派上了用场。

napi_get_last_error_info 是一个函数,它能够帮助您获得关于最后一个发生的 N-API 调用错误的信息。每当 N-API 函数失败时,Node.js 都会存储有关错误的详细信息,包括错误代码、错误消息等。

让我们看一下如何使用 napi_get_last_error_info 和它有什么作用:

使用 napi_get_last_error_info 的步骤:

  1. 当您调用一个 N-API 函数且它返回一个表示失败的结果时(通常是一个错误码)。
  2. 然后您可以立即调用 napi_get_last_error_info 来获取错误详情。
  3. 此函数将填充一个你提供的 napi_extended_error_info 结构体,其中包含了错误信息。

napi_get_last_error_info 的实际例子:

假设您正在编写一个 Node.js 原生插件,该插件需要从 Node.js 接收一些数据。您可能会调用一个 N-API 函数来读取这个数据,比如 napi_get_value_string_utf8

##include `<`node_api.h>

// 假设我们已经有了一个 napi_value 变量 `value` 代表JS传过来的字符串
napi_status status;
size_t result;
char* buffer;

// 尝试获取字符串长度
status = napi_get_value_string_utf8(env, value, NULL, 0, &result);

// 判断是否成功执行
if (status != napi_ok) {
  // 如果失败,则获取错误信息
  const napi_extended_error_info* error_info;
  napi_get_last_error_info(env, &error_info);

  // 输出错误信息
  printf("Error code: %d\n", error_info->error_code);
  printf("Error message: %s\n", error_info->error_message);
}

在这个例子中:

  • 我们首先尝试通过调用 napi_get_value_string_utf8 来获取字符串长度。
  • 如果状态码 status 不是 napi_ok,意味着函数调用失败。
  • 接着我们调用 napi_get_last_error_info 获取更多关于错误的信息。
  • 我们使用 napi_extended_error_info 结构体来获取错误代码和消息。
  • 最后打印出错误信息。

通过这种方式,使用 napi_get_last_error_info 可以帮助开发者诊断 N-API 函数调用失败的原因,并根据错误信息来修复问题。

记住,这是在编写 C 或 C++扩展时使用的函数。在 JavaScript 代码中,您通常不会直接与这些底层 API 打交道。这是为那些想要扩展 Node.js 能力,通过编写接近硬件层面代码的高级用户准备的。

Exceptionsopen in new window

Node.js 中的 N-API 是一个用于构建本地插件的接口。它允许你使用 C 或者 C++ 代码与 Node.js 进行交互,这样可以在性能要求较高的场景中运用更高效的代码。在 v21.7.1 版本的文档中,“Exceptions”指的是在你的本地插件代码中如何处理和抛出错误。

异常(Exceptions)是一种在程序执行过程中遇到不正常情况时通知运行环境的机制。在 JavaScript 中,通常会使用 throw 关键字来抛出一个异常,并且可以用 try...catch 结构来捕获并处理这些异常。

当你在编写使用 N-API 的本地插件时,你也需要正确处理可能发生的异常,这包括两个方面:

  1. 将 C/C++ 中的错误转换为 JavaScript 异常。
  2. 捕获 JavaScript 异常并在本地代码中进行处理。

下面是几个关于在 Node.js N-API 中如何处理异常的例子:

抛出一个异常

假设你有一个本地函数,你想在某种错误条件下将其转换为 JavaScript 异常。

C++ 代码示例:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    // 假设发生了某种错误
    bool has_error = true;

    if (has_error) {
        // 创建一个 JavaScript 错误对象
        napi_value error;
        napi_create_error(env, nullptr, napi_value("发生错误"), &error);

        // 抛出这个错误对象
        napi_throw(env, error);
    }

    // 返回未定义(如果没有错误)
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

捕获并处理异常

现在假设你调用了一个可能会抛出异常的 JavaScript 函数,并且你想在你的本地代码中捕获并处理这个异常。

C++ 代码示例:

##include `<`node_api.h>

void CallJavaScriptFunction(napi_env env) {
    // 获取全局对象
    napi_value global;
    napi_get_global(env, &global);

    // 获取你想调用的 JavaScript 函数(比如 'someFunction')
    napi_value js_function;
    napi_get_named_property(env, global, "someFunction", &js_function);

    // 调用这个函数
    napi_value result;
    napi_status status = napi_call_function(env, global, js_function, 0, nullptr, &result);

    // 检查是否在调用期间抛出了异常
    if (status != napi_ok) {
        // 从环境中获取异常
        napi_value exception;
        napi_get_and_clear_last_exception(env, &exception);

        // 处理这个异常,或者重新抛出
        // (例如打印错误信息)
    }
}

在上面的第二个例子中,我们尝试调用一个 JavaScript 函数,如果抛出异常,我们会通过 napi_get_and_clear_last_exception 捕获它,并决定如何处理。

总而言之,在 Node.js N-API 中处理异常涉及创建 JavaScript 错误对象、抛出它们以及在本地代码中捕获和处理 JavaScript 抛出的异常。正确地处理异常对于编写稳定且可靠的原生插件至关重要。

napi_throwopen in new window

Node.js 中的napi_throw是 Node.js 提供的一个 API,它属于 N-API(原生 API),这是一个用 C 或 C++编写原生插件的接口。napi_throw的作用是在原生代码中抛出一个 JavaScript 错误。

当你使用 Node.js 进行开发时,大部分时间你可能都是在用 JavaScript 编写代码。然而,有时候你可能需要调用一些底层系统级别的功能,或者想要提高程序的性能,这个时候就可以用 C 或 C++来编写那部分代码,然后通过 N-API 与你的 Node.js 应用程序交互。

下面我将通过一个简单的例子解释napi_throw的工作原理:

假设你正在编写一个 Node.js 的扩展模块,该模块需要对传入的字符串参数进行校验,如果传入的不是字符串,则应该抛出一个错误。你的 C 函数可能会像这样:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value argv[1];
  napi_status status;

  // 获取函数参数
  status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "获取参数失败!");
    return NULL;
  }

  // 检查参数类型是否为字符串
  napi_valuetype val_type;
  status = napi_typeof(env, argv[0], &val_type);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "无法检查参数类型!");
    return NULL;
  }

  if (val_type != napi_string) {
    // 如果参数不是字符串,抛出TypeError
    napi_throw_type_error(env, NULL, "参数必须是一个字符串!");
    return NULL;
  }

  // 这里是处理字符串的其他逻辑...

  // 假设一切顺利,你返回了某个结果
  napi_value result;
  // ... 创建result值 ...
  return result;
}

// 初始化函数和导出模块...

在上面的例子中,我们先通过napi_get_cb_info获取 JavaScript 传给函数的参数。然后,我们检查这个参数是否为字符串类型,如果不是,我们使用napi_throw_type_error抛出一个 TypeError 异常。注意,这里我们用的是特殊的napi_throw_type_error而不是napi_throw,因为我们想要抛出的是具体的 TypeError 异常。

实际上,napi_throw更加通用,你可以使用它直接抛出一个自定义的错误对象。例如:

napi_value error_object;
// ... 创建或获取某个错误对象 ...
status = napi_throw(env, error_object); // 抛出这个错误对象

总结一下,napi_throw就是一个让你在原生模块代码中抛出 JavaScript 错误的工具,它让你能够告知 JavaScript 运行时:这里有个问题,处理流程应该停下来,并以异常的方式告诉调用方。在实际应用中,正确地处理错误并抛出异常是非常重要的,因为它有助于保持代码的鲁棒性并使得调试更加容易。

napi_throw_erroropen in new window

napi_throw_error 是 Node.js 中 N-API(Node.js API)的一部分,它允许本地插件的开发者(通常是使用 C 或者 C++语言编写的代码模块)在发生错误时向 JavaScript 抛出异常。N-API 是一个稳定的 API 层,允许你构建原生插件并保证在 Node.js 的不同版本间保持兼容性。

当你在 JavaScript 代码中遇到错误时,可能会用到 throw 关键字来抛出异常,类似地,在原生模块中,你可以使用 napi_throw_error 来实现这个目的。当你调用 napi_throw_error 后,当前执行的 Node.js 函数会停止,并且这个错误会被返回给调用这个原生模块的 JavaScript 代码。

下面是 napi_throw_error 的基本用法:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;

    // ... 一些代码逻辑 ...

    // 假设出现了一个错误,我们需要抛出它
    if (发现错误) {
        status = napi_throw_error(env, NULL, "这里是错误信息");
        if (status != napi_ok) {
            // 处理无法抛出错误的情况
        }
        return NULL;
    }

    // 其他逻辑...

}

在上面的例子中,我们有一个名为 MyFunction 的函数,它可能是作为一个原生模块暴露给 JavaScript 的功能。如果在函数内部检测到了错误条件,我们就使用 napi_throw_error 来抛出一个错误。第一个参数 env 是一个环境变量,它提供了当前 Node.js 调用的上下文。第二个参数是错误码,这里传递 NULL 表示我们没有指定错误码。最后一个参数是一个人类可读的错误信息字符串。

例如,假设你正在编写一个原生模块,用来处理图像,但是如果传入的文件路径不存在,你就需要报告一个错误:

##include `<`node_api.h>
#include `<`stdio.h>

napi_value ProcessImage(napi_env env, napi_callback_info info) {
    napi_status status;
    char filename[100];

    // 假设我们通过某些方式获取到了文件名到 filename 变量

    // 检查文件是否存在
    if (fopen(filename, "r") == NULL) {
        // 文件不存在,抛出错误
        status = napi_throw_error(env, NULL, "文件不存在或无法打开!");
        if (status != napi_ok) {
            // 处理无法抛出错误的情况
        }
        return NULL;
    }

    // 处理图像...

    // 返回结果...
}

在这个例子中,如果尝试打开文件失败了,那么 fopen 会返回 NULL,然后我们调用 napi_throw_error 抛出一个错误,错误信息是 "文件不存在或无法打开!"。当这个原生模块的这个函数被 JavaScript 代码调用时,如果遇到这个错误,JavaScript 调用者可以捕获这个异常并作出相应的处理。

napi_throw_type_erroropen in new window

当然,让我来详细解释一下 napi_throw_type_error 这个函数在 Node.js 中的作用。

Node.js 的 N-API 是一个 C 语言的接口,它允许你使用 C 或 C++编写扩展,并与 JavaScript 交互。这使得开发者能够为 Node.js 编写性能敏感的操作,比如直接与操作系统或其他底层系统交互。

napi_throw_type_error 是 N-API 中提供的一个函数,用于在原生代码中抛出一个 JavaScript 类型错误(TypeError)。类型错误通常发生在值不是预期类型时,比如当期望一个字符串(String)而实际上得到了一个数字(Number)。

下面是一个简单的例子,说明了如何在 N-API 扩展中使用 napi_throw_type_error

想象我们正在创建一个 N-API 模块,该模块包含一个函数,此函数要求传入一个字符串参数。如果调用者传入非字符串参数,我们就抛出一个类型错误。

##include `<`node_api.h>

// 原生函数,期望接收一个字符串参数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1];
    napi_status status;

    // 获取JavaScript传递的参数
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Failed to parse arguments");
        return NULL;
    }

    // 检查参数类型是否为字符串
    napi_valuetype valuetype;
    status = napi_typeof(env, argv[0], &valuetype);
    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Failed to identify argument type");
        return NULL;
    }

    // 如果不是字符串,则抛出类型错误
    if (valuetype != napi_string) {
        napi_throw_type_error(env, NULL, "Argument must be a string.");
        return NULL;
    }

    // ... 进行其它操作 ...

    // 返回undefined表示函数执行成功但没有返回值
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这段代码中,我们定义了一个名为 MyFunction 的 C 函数,它将被暴露给 JavaScript 环境并且可以从 Node.js 代码中调用。如果调用者没有传递一个字符串参数,napi_typeof 函数会检测到这个问题,然后我们调用 napi_throw_type_error 来抛出类型错误。

现在,假设你已经编译了这个 N-API 模块并命名为 my_native_module。在 Node.js 代码中,你可以这样调用它:

const nativeModule = require("./my_native_module");

try {
  nativeModule.myFunction(123); // 故意传入一个非字符串参数
} catch (err) {
  console.error(err); // 这里将捕获并打印出类型错误
}

在这个 JavaScript 示例中,我们尝试使用一个数字去调用 myFunction。因为我们的原生函数期望一个字符串,所以它使用 napi_throw_type_error 抛出一个错误,然后在 JavaScript 层面,我们通过 try-catch 结构捕获并处理这个错误。

希望这个解释和例子可以帮助你理解 napi_throw_type_error 在 Node.js 中的应用。

napi_throw_range_erroropen in new window

Node.js 中的 napi_throw_range_error 是一个函数,属于 Node.js 的 N-API(原生 API),它允许本地模块(用 C 或 C++编写的扩展)与 JavaScript 代码交互。当你需要从 C/C++代码中抛出一个 JavaScript 的 RangeError 异常时,可以使用这个函数。

在详细解释之前,首先要了解几个关键概念:

  1. N-API: 这是一个独立于具体版本的 Node.js API,意在减少原生插件作者因 Node.js 升级导致的维护成本,并提供 ABI(应用程序二进制接口)稳定性。

  2. RangeError: 在 JavaScript 中,RangeError 是一种错误对象,表示值不在其允许的范围或者长度之内时发生的错误。

  3. C/C++ 原生模块: 通常指的是使用 Node.js 提供的 N-API 编写的、可以被 Node.js 直接调用的模块,这些模块是用 C 或 C++语言编写的。

现在,我们来更加详细地看看 napi_throw_range_error

napi_status napi_throw_range_error(napi_env env,
                                   const char* code,
                                   const char* msg);
  • env: 这个参数是指向 N-API 环境的指针,这个环境包含了所有 N-API 调用所需的状态信息。
  • code: 这是一个字符串,代表错误的标识码。它对于区分不同的错误类型很有用。
  • msg: 这也是一个字符串,含有描述错误详情的文本信息。

当你调用 napi_throw_range_error 时,它会创建一个新的 RangeError 对象,并使用你提供的 codemsg 来初始化这个错误对象。然后,它会抛出这个错误,就像在 JavaScript 代码中使用 throw new RangeError(msg); 一样。

实际运用示例

假设你正在编写一个原生模块,该模块提供一个函数,这个函数要求输入一个数组和一个索引值,返回数组中对应索引位置的元素。如果传入的索引超出了数组范围,你想要抛出一个 RangeError

##include `<`node_api.h>

// 原生函数实现
napi_value GetArrayElement(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2];

    // 获取JavaScript传递的参数
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查是否传入了足够的参数,以及参数的类型是否正确
    // ...

    // 假设已经获取了array和index,并对它们进行了验证

    bool is_out_of_range = false;
    // ... 判断索引是否超出数组范围的代码 ...

    if (is_out_of_range) {
        // 索引超出范围,抛出RangeError
        napi_throw_range_error(env, "ERR_INDEX_OUT_OF_RANGE", "Index is out of range.");
        return NULL; // 返回NULL表示发生了异常
    }

    // 其他处理代码,返回数组中的元素 ...
}

// 初始化模块,注册原生函数
NAPI_MODULE_INIT() {
    napi_value getArrayElementFn;
    napi_create_function(env, NULL, 0, GetArrayElement, NULL, &getArrayElementFn);
    napi_set_named_property(env, exports, "getArrayElement", getArrayElementFn);
    return exports;
}

在上面的代码中,如果判断索引超出了数组范围,我们就调用 napi_throw_range_error 抛出异常。这会立即停止当前的函数执行,并将控制权返回给 JavaScript 调用者。JavaScript 调用者可以通过 try-catch 结构捕获并处理这个异常。

这样一来,使用你编写的原生模块的开发者就能得到清晰的错误信息,并且能够像处理常规 JavaScript 异常一样处理这些错误。

node_api_throw_syntax_erroropen in new window

Node.js 中的 napi_throw_syntax_error 是一个函数,属于 N-API 这套 API 的一部分。N-API 提供了一个 C 语言的接口,允许原生插件(用 C 或 C++编写)与 Node.js 代码交互。这些原生插件通常用于性能关键型任务,比如图像处理或者访问系统底层资源。

napi_throw_syntax_error 函数具体是用来创建并抛出一个 JavaScript 语法错误 (SyntaxError)。在 JavaScript 中,当代码中有语法错误时,比如括号不匹配、关键字使用不当等情况,解释器就会抛出 SyntaxError。然而,在一个原生模块中,如果你想要从 C/C++ 级别上抛出一个类似的错误到 JavaScript 环境,你可以通过 napi_throw_syntax_error 来实现。

函数签名:

napi_status napi_throw_syntax_error(napi_env env, const char* code, const char* msg);
  • env 参数是一个 napi_env 类型的变量,它代表了当前的 Node.js 执行环境。
  • code 参数是一个以 NULL 结尾的字符串,表示错误代码或标识符。
  • msg 参数是另一个以 NULL 结尾的字符串,它描述了错误的详细信息。

返回值:

该函数返回一个 napi_status 类型的值,表示操作成功与否:

  • 如果函数执行成功,它将返回 napi_ok
  • 如果发生了错误(例如参数无效),它将返回相应的错误码。

实际运用示例:

假设你正在编写一个 Node.js 原生模块,要求用户传入一段 JSON 字符串来处理。如果传入的字符串格式错误,你希望抛出一个语法错误提醒用户。

下面是一段可能的 C/C++ 代码片段,展示了如何使用 napi_throw_syntax_error

##include `<`node_api.h>

napi_value ParseJSON(napi_env env, napi_callback_info args) {
    // 获取传递给这个函数的参数
    size_t argc = 1;
    napi_value argv[1];
    napi_get_cb_info(env, args, &argc, argv, NULL, NULL);

    // 检查参数类型是否为字符串
    napi_valuetype val_type;
    napi_typeof(env, argv[0], &val_type);
    if (val_type != napi_string) {
        napi_throw_type_error(env, NULL, "Argument must be a string.");
        return nullptr;
    }

    // 尝试将传入的字符串解析为 JSON
    // 假设 parseJSONString 是某个自定义的函数,用于解析 JSON
    bool parse_successful = parseJSONString(env, argv[0]);
    if (!parse_successful) {
        // 如果解析失败,则抛出语法错误
        napi_throw_syntax_error(env, NULL, "Invalid JSON string");
        return nullptr;
    }

    // ...
    // 余下的函数逻辑
}

// 在原生模块初始化时注册上述函数
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, ParseJSON, NULL, &fn);
    napi_set_named_property(env, exports, "parseJSON", fn);
    return exports;
}

在这段代码中,ParseJSON 函数首先检查传入的参数是否为字符串,如果不是,它会抛出一个类型错误。如果参数确实是字符串,但无法被正确解析为 JSON,它则会使用 napi_throw_syntax_error 抛出一个语法错误。这样,当 JavaScript 代码调用这个原生模块时,如果提供的 JSON 字符串格式有误,JavaScript 代码将能够捕获到一个 SyntaxError 异常。

napi_is_erroropen in new window

Node.js 中的 N-API 是一个底层的 API,它允许你用 C 或 C++编写扩展。这些扩展可以被 Node.js 直接调用,用于执行那些 JavaScript 不擅长或者速度较慢的任务。napi_is_error是 N-API 提供的一种功能,我们来详细了解一下。

napi_is_error

简单来说,napi_is_error函数用来检查一个napi_value是否表示一个 JavaScript 的 Error 对象。在 JavaScript 中,错误通常用 Error 对象来表示。这个功能对于原生模块的开发者来说很有用,因为它们需要处理从 JavaScript 传递过来的值,并且有时需要区分哪些值是正常值,哪些是错误。

函数签名

napi_status napi_is_error(napi_env env, napi_value value, bool* result);
  • env:环境变量,代表了 Node.js 的运行环境。
  • value:要检查的napi_value
  • result:输出参数,如果value是一个错误对象,*result会设置为true,否则为false

返回值

返回napi_status,它告诉你操作是否成功。如果操作失败,通常意味着有一些严重的问题,比如参数无效等。

实际例子

假设你正在编写一个 Node.js 的原生模块,你可能有一个 C 函数,它接收一个从 JavaScript 传来的参数,你需要确定这个参数是不是一个 Error 对象。

以下是一个使用napi_is_error的 C 代码片段:

##include `<`node_api.h>

// 假设已经获得了napi_env env和napi_value arg

bool is_error;
napi_status status = napi_is_error(env, arg, &is_error);

if (status != napi_ok) {
    // 处理错误,例如可以输出错误日志、抛出异常等
}

if (is_error) {
    // 如果arg是一个Error对象,执行相应的处理
} else {
    // 如果arg不是Error对象,执行其他逻辑
}

在这个例子中,你首先定义了一个布尔变量is_error来存储结果。然后,你调用napi_is_error并传入envargis_error的地址。函数将检查arg是否是一个 Error 对象,并将结果存储在is_error中。之后,根据is_error的值,你可以决定接下来的处理流程。

请记住,这个 API 和其它的 N-API 函数一样,主要用于创建 Node.js 的原生扩展。如果你是一个编程新手,可能还不需要深入到这个层面。但了解 Node.js 能与 C/C++交互,以及如何检查错误是很有用的。随着你逐渐深入学习,可能会遇到需要使用这些高级特性的情况。

napi_create_erroropen in new window

当然,我会尽量详细并通俗地解释 napi_create_error 这个函数及其在 Node.js 中的作用。

什么是 napi_create_error

napi_create_error 是 Node.js 的 N-API(Node.js API)中的一个函数,用于创建一个新的 Error 对象。N-API 是 Node.js 提供给原生插件作者的稳定的 C 接口,让他们可以不必担心 Node.js 版本的变化而频繁更新代码。

JavaScript 中的错误处理是通过 throw 关键字和 try...catch 结构来实现的。在原生模块中,你可能需要创建一个 JavaScript 错误对象并将其传递回 JavaScript 环境以便能够在 JavaScript 中处理这些错误。napi_create_error 就是用来在原生代码中创建 JavaScript 错误对象的方法。

napi_create_error 函数参数和返回值

napi_create_error 函数通常接受以下几个参数:

  1. napi_env env: 当前执行环境的句柄,它提供了从原生代码到 Node.js 运行时的上下文。
  2. napi_value code: 错误码,通常是一个字符串或整数,表示错误类型。
  3. napi_value msg: 实际的错误信息,通常是描述错误详情的字符串。

这个函数的返回值是一个 napi_status 类型的枚举值,表示调用是否成功。如果成功,你就可以在之后的代码中使用创建的错误对象。

如何使用 napi_create_error

假设你正在编写一个 Node.js 原生插件,并且需要在检测到某种错误情况时,抛出一个错误给 JavaScript 处理。以下是一个简单的示例:

##include `<`node_api.h>

// 假设我们有一个原生函数,该函数需要在发生错误时抛出异常
napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value error_code, error_message, error_object;

    // 这里你的逻辑代码可能会发现了一个错误,并需要报告它
    bool has_error = true; // 假设我们检测到了一个错误

    if (has_error) {
        // 创建错误码
        status = napi_create_string_utf8(env, "MY_ERROR_CODE", NAPI_AUTO_LENGTH, &error_code);
        if (status != napi_ok) return NULL;

        // 创建错误信息
        status = napi_create_string_utf8(env, "An unexpected error occurred!", NAPI_AUTO_LENGTH, &error_message);
        if (status != napi_ok) return NULL;

        // 创建错误对象
        status = napi_create_error(env, error_code, error_message, &error_object);
        if (status != napi_ok) return NULL;

        // 抛出错误
        napi_throw(env, error_object);
    }

    // 其他正常逻辑...
    return some_successful_result;
}

// 初始化函数,注册 MyFunction
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

在这个例子中,如果 MyFunction 检测到错误,它会创建一个带有错误码 "MY_ERROR_CODE" 和错误信息 "An unexpected error occurred!" 的错误对象。然后,它使用 napi_throw 函数抛出这个错误对象。

总结

napi_create_error 是 Node.js N-API 中用来在原生模块代码中创建 JavaScript 错误对象的函数。通过它,你可以在原生代码中生成错误,并通过 JavaScript 的错误处理机制来捕获和处理这些错误。它对于提供清晰的错误信息和保持 JavaScript 和原生代码之间的兼容性非常重要。

记住,在实现原生模块时,正确的错误处理能够大大提高模块的健壮性和可用性。

napi_create_type_erroropen in new window

Node.js 中的 N-API (Node Addon API) 是一个用于构建本地插件(native addons)的 API 层。本地插件是指直接与 Node.js 运行时集成的,使用 C 或 C++ 编写的模块。这些插件可以提供性能上的优势或者使得 Node.js 能够访问系统级资源和库。

napi_create_type_error 函数是 N-API 中的一部分,它允许你创建一个 JavaScript 的类型错误 (TypeError) 对象。在 JavaScript 中,当一个值不是预期的数据类型时,通常会抛出一个 TypeError。例如,如果函数期望一个字符串参数,但是收到了一个数字,那么就可能抛出一个 TypeError。

使用 napi_create_type_error

在 Node.js 的本地插件中,你有时需要向 JavaScript 代码报告某个值的类型不正确。在这种情况下,你可以使用 napi_create_type_error 来创建一个相应的错误对象,并将它传递回 JavaScript 环境。

以下是如何使用 napi_create_type_error 的步骤:

  1. 确认你正在编写的代码是一个本地插件,即使用 C 或 C++ 并希望与 Node.js 交互。
  2. 包含必要的 N-API 头文件。
  3. 实现一个函数,该函数会检查输入参数的类型,并在类型不符合期望时创建类型错误。

下面是一个简化的例子来演示其用法:

##include `<`node_api.h>

// 假设这是一个函数,它期望第一个参数是一个字符串。
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value this_arg;
    napi_status status;

    // 获取函数参数
    status = napi_get_cb_info(env, info, &argc, args, &this_arg, NULL);
    if (status != napi_ok) return NULL;

    // 检查参数类型
    napi_valuetype valuetype;
    status = napi_typeof(env, args[0], &valuetype);
    if (status != napi_ok) return NULL;

    // 如果不是字符串,则创建 TypeError
    if (valuetype != napi_string) {
        napi_value result;
        napi_value error_message;
        const char* message = "Wrong argument type. Expected a string.";

        // 创建一个JS字符串作为错误信息
        status = napi_create_string_utf8(env, message, NAPI_AUTO_LENGTH, &error_message);
        if (status != napi_ok) return NULL;

        // 创建 TypeError 错误对象
        status = napi_create_type_error(env, NULL, error_message, &result);
        if (status != napi_ok) return NULL;

        // 抛出错误
        napi_throw(env, result);

        return NULL;
    }

    // 如果参数类型正确,继续执行函数...
}

// 初始化代码略过...

在上面的例子中,MyFunction 是一个本地插件中定义的函数,它期望接收一个字符串类型的参数。如果传入的参数不是字符串类型,那么我们使用 napi_create_type_error 来创建一个 TypeError 对象,并通过 napi_throw 来抛出这个错误。

总结

napi_create_type_error 是 Node.js N-API 的一部分,用于在本地插件中创建并抛出 JavaScript 的 TypeError。这对于确保 JavaScript 从本地插件接收到正确类型的数据非常有用,并且有助于开发者诊断和处理错误。

napi_create_range_erroropen in new window

Node.js 中的napi_create_range_error是一个功能,它属于 N-API(原生 API),这是一套 C 语言编写的接口,允许你创建和操作 JavaScript 值,并在原生插件(用 C/C++编写的模块)中与 JavaScript 代码交互。使用 N-API 编写的原生模块可以在不同版本的 Node.js 上运行而无需重新编译,这有助于提高模块的兼容性和稳定性。

现在,来解释一下什么是napi_create_range_error

在 JavaScript 中,RangeError是一种错误对象,表示当一个值不在其预期范围内时抛出的错误。例如,如果你尝试用一个负数去访问数组的元素,或者使用Number对象的方法传递了一个无效的数字范围,就会抛出RangeError

使用napi_create_range_error,你可以从 C/C++代码中创建一个与 JavaScript RangeError相对应的 N-API 错误对象。这在你需要将一个由原生代码检测到的范围错误传达给 JavaScript 环境时非常有用。

这个函数的签名如下:

napi_status napi_create_range_error(napi_env env,
                                    napi_value code,
                                    napi_value msg,
                                    napi_value* result);
  • env: 这是一个代表 N-API 环境的句柄,你可以通过它来调用 N-API 的其他函数。
  • code: 一个napi_value,包含了一个错误码,通常是一个字符串 ID。
  • msg: 一个napi_value,包含了一个描述错误的消息。
  • result: 这是一个输出参数,指向创建好的RangeError对象。

举个例子,假设你正在编写一个原生插件,需要检查传入的参数是否在有效的索引范围内:

##include `<`node_api.h>

napi_status CheckIndexInRange(napi_env env, int index, int array_size) {
    if (index `<` 0 || index >= array_size) {
        // 创建一个错误消息
        napi_value msg;
        napi_create_string_utf8(env, "Index is out of range", NAPI_AUTO_LENGTH, &msg);

        // 创建一个 RangeError 对象
        napi_value range_error;
        napi_create_range_error(env, NULL, msg, &range_error);

        // 将这个错误抛给 JavaScript 环境
        napi_throw(env, range_error);

        return napi_throw_error; // 返回一个表示发生了错误的状态码
    }
    return napi_ok; // 没有错误发生
}

在上面的代码中,当CheckIndexInRange函数检测到index不在合法范围内时,它会创建一个RangeError并使用napi_throw将这个错误抛出到 JavaScript 环境中。这样,当 JavaScript 代码调用这个原生模块的相关函数并发生范围错误时,JavaScript 代码就能捕获到这个错误并相应地处理。

node_api_create_syntax_erroropen in new window

好的,Node.js 中的 node_api_create_syntax_error 是一个关于 N-API 的功能点。N-API 是 Node.js 提供的一套用于构建原生插件的 API,这些 API 是独立于 JavaScript 运行时的,并且在 Node.js 的不同版本中保持向后兼容。

node_api_create_syntax_error 函数用于创建一个新的 SyntaxError 对象。SyntaxError 是 JavaScript 中的一个错误类型,它表示在解析代码时发生的语法错误。当 JavaScript 引擎读取和解释代码而遇到不能理解的结构时,就会抛出 SyntaxError

该函数的具体作用是在原生代码(比如 C 或 C++模块)中创建一个与 JavaScript SyntaxError 相似的错误对象,然后可以将这个错误传回到 JavaScript 环境中。这样做的目的是为了让原生模块能够以一种对 JavaScript 开发者来说熟悉的方式报告错误。

使用 node_api_create_syntax_error 时,通常会指定三个参数:

  1. env - 当前的 N-API 环境句柄,它提供了大量实用的 API。
  2. code - 错误码,是一个唯一标识符,用于区分不同的错误。
  3. msg - 实际的错误信息,描述了发生了什么错误。

现在我们来看几个简单的例子:

例子 1: 创建一个简单的 SyntaxError

假设你正在编写一个原生模块,需要检测传入的字符串是否符合你预期的格式。如果字符串格式不正确,你想要抛出一个 SyntaxError

##include `<`node_api.h>

napi_value CreateSyntaxErrorExample(napi_env env, napi_callback_info info) {
  napi_value syntax_error;

  // 创建一个 SyntaxError 对象
  const char* error_message = "Unexpected token in JSON at position 10";
  napi_status status = napi_create_syntax_error(env, NULL, error_message, &syntax_error);

  // 检查操作是否成功
  if (status != napi_ok) {
    // 处理错误...
  }

  // 将创建的错误返回给 JavaScript
  return syntax_error;
}

// 更多的模块初始化代码...

这段代码定义了一个函数 CreateSyntaxErrorExample,它创建了一个包含自定义错误消息的 SyntaxError 对象,并将其返回给 JavaScript。若创建失败,应当处理相应的错误状态。

例子 2: 在检测到无效输入时使用 SyntaxError

##include `<`node_api.h>

napi_value ParseJSON(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1];
  napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

  // 以下略过参数检验和转换的代码

  // 假设我们已经获取了要解析的字符串:json_str

  bool is_valid_json = false;
  // 对 json_str 进行处理并设置 is_valid_json 标志

  if (!is_valid_json) {
    // 如果不是有效的 JSON,则创建 SyntaxError 并将其抛出
    napi_value syntax_error;
    status = napi_create_syntax_error(env, NULL, "Invalid JSON string", &syntax_error);

    if (status == napi_ok) {
      napi_throw(env, syntax_error); // 抛出错误
    }
  }

  // 解析成功的逻辑...
}

// 更多的模块初始化代码...

在这个示例中,ParseJSON 函数试图解析一个 JSON 字符串。如果检测到该字符串不是有效的 JSON,它将创建一个 SyntaxError 对象并通过调用 napi_throw 将其抛给调用方。这样,JavaScript 调用侧就可以捕获并处理这个错误了。

napi_get_and_clear_last_exceptionopen in new window

napi_get_and_clear_last_exception 是 Node.js 中的一个函数,它属于 N-API 的一部分。N-API 是一个稳定的 C API,允许你构建原生插件,即用 C、C++等语言编写的模块,这些模块可以直接被 Node.js 代码调用。

当你在创建 Node.js 的原生插件时,可能会与 JavaScript 代码进行交互,并且有时候这些交互可能会导致异常(错误)发生。正常情况下,如果 JavaScript 代码抛出了一个异常,而你没有恰当地处理它,那么这个异常就会沿着调用栈向上传播,直到它到达最顶层并被 Node.js 环境捕获。

napi_get_and_clear_last_exception 这个函数的作用是:当你的原生代码引起了 JavaScript 异常时,你可以使用此函数来检索这个异常对象,并且同时清除掉这个异常,防止它继续向上传播。

以下是如何使用 napi_get_and_clear_last_exception 的一个简单例子:

假设我们有一个原生函数,该函数试图执行一段 JavaScript 代码,但可能会抛出一个异常:

##include `<`node_api.h>

// 假设的原生函数,其中可能会产生JavaScript异常
napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    napi_value result;

    // 尝试做一些工作...
    // 这里的某些操作可能会抛出JavaScript异常

    // 检查是否有异常发生
    napi_value exception;
    napi_status status = napi_get_and_clear_last_exception(env, &exception);
    if (status == napi_ok) {
        // 异常已经被清理,现在你可以决定如何处理它
        // 例如,你可以打印出错误信息或将其作为错误返回值返回给调用者
    }

    // 其他工作...

    return result;
}

在这个例子中,MyNativeFunction 函数在执行期间可能会产生一个异常。通过调用 napi_get_and_clear_last_exception,我们检查是否有任何异常发生,并清除它。如果确实存在异常,我们就可以获取异常对象,并决定如何处理它,比如记录日志,或者将错误信息返回给原生函数的调用者。

此外,N-API 提供了一套完整的异常处理 API,包括但不限于获取异常信息、创建新的错误对象等,让原生模块开发者能够更好地控制错误处理流程。

记住,N-API 是专门设计用来构建原生插件的,所以如果你只是在写普通的 Node.js JavaScript 代码,可能永远都不需要直接接触到像napi_get_and_clear_last_exception这样的函数。这些 API 更多地用在底层的性能优化或特殊功能实现上。

napi_is_exception_pendingopen in new window

好的,让我来解释一下 Node.js 中的 napi_is_exception_pending 函数。

N-API(Node.js API)是一个用于构建原生插件的接口。原生插件指的是用 C、C++等编程语言写成的模块,它们能够直接与 Node.js 的底层 API 进行交互,通常用于性能关键型操作或者调用系统级别的库。

在使用 N-API 编写原生代码时,可能会产生异常。在 JavaScript 中,异常通常是一个错误对象,当你的代码执行出错时,JavaScript 运行时会抛出这个错误对象。类似地,在 N-API 编写的原生插件中也可以抛出异常。

napi_is_exception_pending 是一个函数,用来检查是否有异常在原生模块中被触发但还未被 JavaScript 处理。这个函数非常重要,因为在继续执行其他 N-API 调用前,必须清理现有的异常,否则可能导致不可预见的行为。

下面我们看一下 napi_is_exception_pending 函数的基本使用方法:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
  // ...你的代码...

  // 检查是否有未处理的异常
  bool hasException;
  napi_status status = napi_is_exception_pending(env, &hasException);

  // 如果调用成功,并且有异常存在
  if (status == napi_ok && hasException) {
    // 处理异常,比如获取异常对象并打印它
    napi_value exception;
    status = napi_get_and_clear_last_exception(env, &exception);

    // ... 打印异常信息或其他处理 ...

    // 返回一个错误或null表示函数执行遇到了问题
    return NULL;
  }

  // ... 其他代码,如果没有异常就继续执行 ...

  // 返回结果
  napi_value result;
  // 假设我们要返回一个简单的字符串 "hello world"
  status = napi_create_string_utf8(env, "hello world", NAPI_AUTO_LENGTH, &result);

  return status == napi_ok ? result : NULL;
}

// 注册上面的函数为一个原生模块的导出函数
NAPI_MODULE_INIT() {
  napi_value fn;
  napi_status status = napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
  if (status != napi_ok) return NULL;

  napi_set_named_property(env, exports, "myFunction", fn);
  return exports;
}

在这个例子中,我们定义了一个名为 MyFunction 的函数,它首先使用 napi_is_exception_pending 来检查是否有异常在等待处理。如果有异常,我们利用函数 napi_get_and_clear_last_exception 获取这个异常对象,并且可以做进一步处理,例如打印异常信息。此外,如果没有异常就继续后面的操作,并最终创建一个字符串返回结果。

通过这种方式,我们在编写原生插件的时候能够更好地管理和响应异常情况,确保 Node.js 应用的稳定性和正确性。

napi_fatal_exceptionopen in new window

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的 JavaScript 运行环境,允许你在服务器端运行 JavaScript 代码。Node.js 使用事件驱动、非阻塞 I/O 模型,使其轻量且高效,非常适合处理数据密集型实时应用。

在 Node.js 中,有一个原生模块系统叫作 N-API,它提供了一系列的 API,让你能够使用 C 或者 C++ 来编写扩展。这些扩展能够直接与 Node.js 的 JavaScript 运行时进行交互,提供一些 JavaScript 本身无法或者不那么高效实现的功能。

napi_fatal_exception 是 N-API 中的一个函数,当你在编写一个 Node.js 的原生扩展时可能会用到它。简单来说,当你的原生扩展中发生了一个无法恢复的错误,可以调用此函数来通知 Node.js 异常情况,并让 Node.js 停止当前的事件循环并退出。

下面是 napi_fatal_exception 的一个简化示例说明:

想象你正在编写一个原生模块,该模块需要执行一些底层操作,比如访问硬件信息或者进行复杂的数学计算。假设在执行这些操作时出现了一个严重错误,例如访问了无效的内存地址或者触发了一个断言失败。在这种情况下,可能需要立即终止程序,因为程序的状态已经不确定且可能导致更多错误或安全问题。

##include `<`node_api.h>

// 一个示例原生函数,它可能会遇到致命的异常
napi_value ExampleFunction(napi_env env, napi_callback_info info) {
    // 假设我们在这里进行一些操作,但出现了一个严重错误...
    // ...

    // 致命错误发生,我们需要通知 Node.js
    napi_value error;
    napi_create_string_utf8(env, "致命错误:操作无法完成!", NAPI_AUTO_LENGTH, &error);
    napi_fatal_exception(env, error);

    // 此行之后的代码将不会被执行,因为 napi_fatal_exception 将终止执行
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;
    napi_create_function(env, NULL, 0, ExampleFunction, NULL, &fn);
    napi_set_named_property(env, exports, "exampleFunction", fn);
    return exports;
}

// 注册模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上述示例中,如果 ExampleFunction 函数中的操作失败并触发了致命的异常,它会创建一个错误信息字符串并通过 napi_fatal_exception 传递给 Node.js。一旦调用了 napi_fatal_exception,Node.js 会认为发生了不可恢复的错误,并将默认终止进程。

值得注意的是,在真实的原生模块开发中,你不会频繁地使用 napi_fatal_exception,因为大部分错误都应该通过正常的 JavaScript 异常机制来处理。只有在确实遇到了无法恢复的严重错误时,才应该使用此函数。

Fatal errorsopen in new window

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的 JavaScript 运行环境,它使得开发者可以使用 JavaScript 编写服务端代码。在 Node.js 中提供了很多的内建模块,以及通过 npm(Node Package Manager)可以安装的第三方模块,以便开发者构建各种应用程序。

N-API 是 Node.js 提供的一个用来构建原生扩展的 API。原生扩展是一种特殊类型的模块,它允许 JavaScript 与能够以更高性能执行特定任务的 C/C++代码互操作。这通常用于那些对性能要求很高的场景,比如访问硬件功能或者进行密集型计算。

当你遇到 "Fatal errors" 这个术语时,它指的是在运行 Node.js 程序过程中,系统遇到了无法恢复的错误,导致 Node.js 进程崩溃。下面就来简单解释一下什么是 Fatal errors,并给出一些实际的例子。

Fatal errors

在 Node.js 的 N-API 上下文中,Fatal error 是指在使用 N-API 函数调用时,如果因为某种原因(比如内存不足、无效的参数等)导致程序无法继续执行,Node.js 就会触发一个致命错误。

它通常意味着有一些重大的问题出现了,必须立即修复,因为它会导致 Node.js 进程完全退出,而且不会触发正常的异常处理流程。而且,由于这类错误是在 C/C++ 层面产生的,JavaScript 层面的 try-catch 无法捕捉到它们。

实际的例子:

  1. 内存不足: 如果你的原生扩展尝试分配内存,但是系统没有足够的可用内存,这可能会导致致命错误。

    Napi::Env env = ...;
    size_t size_to_allocate = 1024 * 1024 * 1024; // 尝试分配 1GB 内存
    char* large_block = new (std::nothrow) char[size_to_allocate];
    if (large_block == nullptr) {
      // 内存分配失败,触发Fatal error
      NAPI_FATAL_ERROR("Unable to allocate enough memory");
    }
    
  2. 违反 API 使用规则: 比如,如果一个 N-API 函数要求传入非空指针作为参数,但你错误地传入了空指针,这可能导致致命错误。

    Napi::Env env = ...;
    napi_value result;
    // 假设 some_function 需要一个有效的 `napi_value` 参数,而不是 `nullptr`
    napi_status status = some_function(env, nullptr, &result);
    if (status != napi_ok) {
      // API 调用不正确,触发Fatal error
      const napi_extended_error_info* error_info;
      napi_get_last_error_info(env, &error_info);
      NAPI_FATAL_ERROR(error_info->error_message);
    }
    
  3. 未处理的异常: 如果你的原生扩展函数中发生了未被捕获的异常,它也可能转变成致命错误。

    Napi::Env env = ...;
    try {
      // 执行一些可能抛出异常的操作
    } catch (const std::exception& e) {
      // 异常被捕获,但没有合适的处理方式,触发Fatal error
      NAPI_FATAL_ERROR(e.what());
    }
    

在处理致命错误时,你需要谨慎,确保只有在真正无法恢复的情况下才触发它们,并且最好在发生之前,就通过代码审查和测试来预防这类错误的发生。因为一旦发生,它通常意味着进程的即刻终止。

napi_fatal_erroropen in new window

Node.js 中有一种叫 N-API 的 API(应用程序编程接口),它允许你使用 C 或 C++ 编写代码,这些代码可以和 Node.js 进行交互。N-API 设计的目的是帮助开发者创建所谓的原生插件或原生模块,即直接与 Node.js JavaScript 引擎通信的非 JavaScript 代码。

napi_fatal_error 是 N-API 提供的一个函数,它的作用是在一个不可恢复的错误发生时通知 Node.js。当这个函数被调用后,它会导致 Node.js 进程立即终止,也就是说你的 Node.js 应用会立刻停止运行,并输出一条错误消息。

这个函数的定义如下:

void napi_fatal_error(const char* location, size_t location_length,
                      const char* message, size_t message_length);

参数说明:

  • location: 这个参数是一个字符串,表示错误发生的位置,可能是一个文件名或者函数名。
  • location_length: 是上述位置字符串的长度。
  • message: 这个参数是一个字符串,包含了错误信息。
  • message_length: 是上述错误信息字符串的长度。

这里要注意的是,napi_fatal_error 通常只在最糟糕的情况下使用,当你确实没有其他方法来处理错误时。因为一旦调用这个函数,你的应用将不会有机会进行任何形式的清理工作,比如关闭文件句柄、数据库连接等。

举例来说,假设你正在编写一个原生插件来读取某种特殊格式的文件,而这种格式的解析库只提供了 C++ 接口。在解析文件的过程中,如果发现了一个根本无法解析的错误(比如文件已损坏到无法修复的地步),你就可以使用 napi_fatal_error 来报告这个问题:

##include `<`node_api.h>

void readFileAndParse(napi_env env, napi_callback_info info) {
    // 假设 params 包含了我们需要的参数
    // ...

    if (file_is_corrupted(params)) {
        // 如果文件损坏了,并且我们无法处理这个错误
        const char* location = "readFileAndParse";
        const char* message = "The file is corrupted and cannot be read.";
        napi_fatal_error(location, NAPI_AUTO_LENGTH, message, NAPI_AUTO_LENGTH);
        // 调用 napi_fatal_error 后,下面的代码不会被执行
    }

    // 正常的文件处理代码
    // ...
}

请记住,napi_fatal_error 应该是最后的手段。在大多数情况下,优雅地处理错误(如返回异常给 JavaScript)是更好的做法,因为这允许你的应用有机会恢复或至少以适当的方式退出。

Object lifetime managementopen in new window

在 Node.js 中,N-API 是一个用于构建本地插件的 API。它允许你使用 C 或者 C++ 代码来写扩展,这些扩展可以直接与 Node.js 的运行时交互。而“对象生命周期管理”指的是在这个上下文中如何管理 JavaScript 对象和本地资源(C/C++ 分配的内存)的创建、使用和销毁。

在编写 N-API 扩展时,需要格外注意不要造成内存泄露或野指针。这就需要妥善管理你创建的对象的生命周期。Node.js 的垃圾回收器负责管理 JavaScript 对象的生命周期,但对于本地资源并不会自动管理。因此,N-API 提供了一系列的函数来帮助控制本地资源的生命周期。

引用 (Reference)

在 N-API 中,你可以创建对 JavaScript 对象的引用。这种引用保证了对象不会被垃圾回收器回收,直到你明确地告诉它这样做。引用分为两种:

  • 强引用 (napi_ref): 当你创建一个强引用时,相应的对象将保持活跃状态,不会被垃圾回收器回收。
  • 弱引用 (napi_weakref): 弱引用不会阻止对象被回收。当你只想要知道对象是否还活着,但不想影响其生命周期时,可以使用弱引用。

实际例子

假设你正在编写一个 Node.js 模块,该模块需要从 JavaScript 创建一个大型的数据缓存,并将其传递给 C++ 的某个库去处理。

##include `<`node_api.h>

// 假设这是你即将存储 JavaScript 对象引用的变量
napi_ref myObjectRef;

// 这个函数将一个 JavaScript 对象保存为一个强引用,使其不会被回收
void CreateStrongReference(napi_env env, napi_value object) {
    // 创建一个新的强引用
    napi_status status = napi_create_reference(env, object, 1, &myObjectRef);
    if (status != napi_ok) {
        // 处理错误 ...
    }
}

// 当你不再需要那个对象时,可以删除引用以允许垃圾回收器回收它
void DeleteReference(napi_env env) {
    napi_delete_reference(env, myObjectRef);
}

在上面的代码中,我们首先定义了一个全局变量 myObjectRef 来存储对象的引用。然后有两个函数:CreateStrongReference 用于创建一个强引用,它接受一个 JavaScript 对象,并确保它不会被垃圾回收器回收;DeleteReference 用于释放引用,这样垃圾回收器就可以正常地回收对象了。

注意事项

当你使用 N-API 管理对象生命周期时,以下几点非常重要:

  1. 平衡创建和删除引用:每次调用 napi_create_reference 后,都应该在适当的时机调用 napi_delete_reference。否则可能导致内存泄漏。

  2. 遵循最佳实践:避免保留不必要的引用,尤其是强引用,因为它们会影响垃圾回收器的工作,可能导致更多的内存消耗。

  3. 线程安全:在多线程环境中使用 N-API 时,确保相关操作是线程安全的。

综上所述,对象生命周期管理在使用 N-API 编写本地扩展时至关重要,合理地管理不仅可以避免内存泄漏,还能确保程序的性能。

Making handle lifespan shorter than that of the native methodopen in new window

在 Node.js 中,N-API 是一个用来构建原生插件的 API。它允许你使用 C/C++代码与 Node.js 的 JavaScript 引擎交互,以此可以创建性能更优的模块或者直接调用系统级别的 API。

由于 JavaScript 和 C/C++ 在内存管理上有很大的不同,N-API 提供了一套处理这些差异的机制。特别地,在 N-API 中,我们经常需要创建“句柄”(handle),这是指向 JavaScript 对象的引用,通过这些句柄我们可以在 C/C++ 代码中操作 JavaScript 对象。

然而,在某些情况下,句柄的生命周期可能会比它所关联的 C/C++原生方法更长。这就引入了潜在的问题:如果句柄太久没有被释放,它会保持对一个可能已经不再需要的 JavaScript 对象的引用,从而导致该对象无法被垃圾回收器回收,造成内存泄漏。

为了解决这个问题,Node.js v21.7.1 中 N-API 提供了一种方式来确保句柄不会比其对应的原生方法生命周期更长。具体来说,就是通过限制句柄的作用域,只让它存在于需要它的代码执行期间。

让我们举个例子:

假设我们正在编写一个原生模块,该模块负责从操作系统中获取系统时间,并将它返回给 JavaScript 代码。我们的 C++函数可能会看起来像这样:

##include `<`napi.h>
#include `<`time.h>

// 假设这是一个N-API暴露给JS的函数
Napi::Value GetSystemTime(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();

    // 获取系统时间
    time_t current_time = time(nullptr);

    // 这里创建一个句柄,这个句柄关联到一个新的JS Date对象
    Napi::Object dateObject = Napi::Date::New(env, static_cast`<`double>(current_time) * 1000);

    // 返回这个句柄,即返回Date对象给JS
    return dateObject;
}

// 省略其他注册模块的代码...

在这个例子中,dateObject 是一个句柄,它指向了一个新创建的 JavaScript Date 对象。当 GetSystemTime 函数执行完毕并返回这个 Date 对象之后,在 C++代码中,我们不再需要 dateObject 这个句柄了。N-API 的设计可以保证,一旦这个函数返回,除非 JavaScript 代码依然持有这个 Date 对象的引用(例如,把它赋值给一个变量),否则这个 Date 对象和 dateObject 句柄都可以被垃圾回收器回收,避免了内存泄漏。

总的来说,通过调整 N-API 中句柄的生命周期,使得它们不会比相关的原生方法活得更久,Node.js V21.7.1 帮助开发者更好地管理内存,防止内存泄漏的问题。这对于编写高效、稳定的原生扩展至关重要。

napi_open_handle_scopeopen in new window

Node.js 中的 napi_open_handle_scope 是一个与 N-API(Node.js API)相关的功能。在深入了解这个函数之前,先解释一下几个概念,这样你可以更好地理解它的作用。

N-API(Node API) N-API 是一个 C 语言的 API 层,它允许你编写能够跨不同版本 Node.js 运行的本地插件。使用 N-API 编写的本地插件不依赖于 V8 引擎的内部结构,因此当 Node.js 升级时,很可能无需修改代码就能继续工作。

句柄(Handle) 在 Node.js 中,句柄是对 JavaScript 值的引用,它们在 N-API 中用于管理和维护 JavaScript 对象的生命周期。句柄使得 C/C++代码可以存储和操作 JavaScript 对象。

句柄作用域(Handle Scope) 句柄作用域提供了一种限制句柄生命周期的方式,它可以确保在这个作用域结束时,所有创建的句柄都会被正确的清除。这有助于避免内存泄露。

现在,来看 napi_open_handle_scope 这个函数的作用:

  • 当一个本地插件(用 C 或 C++编写的代码)要与 JavaScript 交云时,它可能需要创建许多 JavaScript 对象。napi_open_handle_scope 用于创建一个新的句柄作用域。在这个作用域中,你可以安全地创建 JavaScript 对象并且与它们交互。
  • 在执行完相关操作后,你需要调用 napi_close_handle_scope 函数来关闭作用域并且释放其中的句柄,以防止内存泄露。

实际运用例子:

假设你正在编写一个本地插件,该插件提供了一个函数,用于创建一个新的 JavaScript 数组并且填充一些数据。

##include `<`node_api.h>

// 一个简单的函数,用来创建一个JavaScript数组并且填充数据
napi_value CreateArray(napi_env env, napi_callback_info info) {
    // 开启一个新的句柄作用域
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope);

    // 创建一个空的JavaScript数组
    napi_value array;
    napi_create_array(env, &array);

    // 填充数组
    for (int i = 0; i `<` 10; i++) {
        napi_value num;
        napi_create_int32(env, i, &num);
        napi_set_element(env, array, i, num);
    }

    // 关闭句柄作用域
    napi_close_handle_scope(env, scope);

    // 返回创建的数组
    return array;
}

在上面的例子中,我们首先开启了一个新的句柄作用域,然后创建了一个 JavaScript 数组,并通过一个循环向其中填充了整数 0 到 9。在完成这些操作后,我们关闭了句柄作用域,以确保所有在作用域中创建的句柄都得到适当的清理。最后,我们返回了这个数组,这样 JavaScript 代码就可以使用它了。

使用 napi_open_handle_scopenapi_close_handle_scope 可以帮助你编写出更稳健、内存高效的本地插件。

napi_close_handle_scopeopen in new window

napi_close_handle_scope 是一个函数,它属于 Node.js 的 N-API(Node.js API),这个 API 提供了一套用 C 语言编写本地插件的接口。在解释 napi_close_handle_scope 之前,我们需要先理解两个概念:handlehandle scope

在 JavaScript 中,当你创建了一个对象、函数或者其他任何类型的值时,JavaScript 引擎会在内存中为其分配一个地址。在 C/C++ 插件代码中操作这些 JavaScript 值时,不能直接使用它们在内存中的地址,而是通过“句柄”(handles)来引用这些值。这样做的目的是为了确保垃圾回收机制能够正确地管理这些值的生命周期,防止内存泄露。

handle scope 则是一个包含多个句柄的容器,当你在 C/C++ 代码中创建了很多 JavaScript 值时,这些值的句柄会被存放在一个handle scope 中。每个handle scope都有自己的生命周期,在这段时间内,可以安全地引用其中的句柄。

现在让我们来看看 napi_close_handle_scope 函数。这个函数用于关闭一个先前打开的 handle scope。每当你在 C/C++ 的 N-API 插件代码中完成了一系列与 JavaScript 值相关的操作,并且不再需要继续访问这些值时,就应该关闭相应的 handle scope。关闭 handle scope 可以告诉 Node.js 的垃圾回收机制,所有在这个 handle scope 中的句柄所引用的 JavaScript 值现在都可以被回收,如果它们不再被程序的其他部分所引用的话。

示例

为了具体说明,我们可以设想这样一个场景:

假设你正在编写一个 Node.js 的 C++ 插件,它需要创建多个新的 JavaScript 对象,并将它们传递给 Node.js 的 JavaScript 环境。

##include `<`node_api.h>

// 这个函数模拟了一个可能会创建很多JavaScript对象的操作
napi_value CreateObjects(napi_env env) {
    napi_handle_scope scope;
    napi_open_handle_scope(env, &scope); // 打开一个 handle scope

    // 接下来创建一些 JavaScript 对象,比如:
    napi_value jsObject1, jsObject2;
    napi_create_object(env, &jsObject1);
    napi_create_object(env, &jsObject2);
    // ... 创建更多对象,并进行相关操作

    // 完成所有操作后,我们不再需要维持这些对象的句柄:
    napi_close_handle_scope(env, scope); // 关闭 handle scope

    // 假设我们要返回最后创建的对象给 JavaScript 环境
    return jsObject2;
}

// 注册上面的函数作为一个模块导出
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

void Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc = { "createObjects", 0, CreateObjects, 0, 0, 0, napi_default, 0 };
    napi_define_properties(env, exports, 1, &desc);
}

在上述示例中,napi_open_handle_scope 函数用于创建一个新的 handle scope。随后,我们可以安全地创建多个 JavaScript 对象。在这些操作完成后,通过调用 napi_close_handle_scope 来关闭 handle scope,这表明所有在此 handle scope 中创建的句柄所指向的对象现在可以被垃圾收集器回收。最后,CreateObjects 函数返回了一个 JavaScript 对象给 Node.js 环境。

注意,以上代码只是一个简化的例子,旨在帮助理解 napi_close_handle_scope 的用法。在实际的 N-API 插件开发中,还需要考虑错误处理和异常安全等因素。

napi_open_escapable_handle_scopeopen in new window

当然,让我们深入了解 Node.js 中的 napi_open_escapable_handle_scope 函数。

首先,N-API 是 Node.js 提供的一个 API 层,允许你用 C 或 C++ 编写扩展。这些扩展可以直接与 Node.js 的底层 V8 JavaScript 引擎交互。这样做的好处之一是编写出来的模块不依赖于 V8 引擎的特定版本,也就是说,它们在 Node.js 的不同版本中更加稳定。

现在让我们聊聊 handle scope。在 V8(Node.js 使用的 JavaScript 引擎)中,大部分的 JavaScript 对象都是通过堆内存管理的。为了高效地回收这些对象,V8 使用了垃圾回收机制。Handle scope 是 V8 用来跟踪和管理 JavaScript 对象生命周期的机制之一。当你创建了很多局部 JavaScript 对象,并且希望这些对象在当前函数调用完成后能够有效地被垃圾回收器清理时,你会使用 handle scope

那么 napi_open_escapable_handle_scope 具体是做什么的呢?简单来说,它创建了一个叫做 escapable handle scope 的新环境,它允许你临时保留一些 JavaScript 对象,即使当前的函数已经结束,这些对象也可以“逃离”这个作用域并被其他部分的代码继续使用。

实际运用示例:

假设你正在编写一个 Node.js 扩展来处理图像,你需要在 C++ 代码中创建一个表示图片的 JavaScript 对象,然后将这个对象传回给 Node.js 的调用者。

##include `<`node_api.h>

// 假设这个函数会在某处被调用,以创建一个新的图片对象
napi_value CreatePictureObject(napi_env env) {
    napi_status status;
    napi_handle_scope scope;

    // 创建一个 escapable handle scope
    status = napi_open_escapable_handle_scope(env, &scope);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 在这个作用域中创建一个新的 JavaScript 对象
    napi_value pictureObj;
    status = napi_create_object(env, &pictureObj);
    if (status != napi_ok) {
        // 处理错误...
    }

    // ...设置对象的属性等...

    // “逃逸”对象,这样它就可以被返回并由函数外部的代码所使用
    napi_value escaped_pictureObj;
    status = napi_escape_handle(env, scope, pictureObj, &escaped_pictureObj);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 关闭 handle scope
    status = napi_close_escapable_handle_scope(env, scope);
    if (status != napi_ok) {
        // 处理错误...
    }

    return escaped_pictureObj; // 返回“逃逸”后的对象
}

// 这个函数可能会被暴露给 Node.js 代码调用
// ...

在上面的例子中,我们使用 napi_open_escapable_handle_scope 创建了一个可以从中“逃逸”的作用域。在这个作用域里,我们创建了一个新的对象 pictureObj,并且通过调用 napi_escape_handle 让这个对象在 napi_close_escapable_handle_scope 调用关闭作用域后仍然可以被使用。

这样,当你的 C++ 函数返回时,Node.js 的调用者就能够获取到这个 JavaScript 对象并与其交互,比如读取属性值或者调用对象上的方法。这是一个非常有用的机制,尤其是在您需要在原生代码中创建复杂对象并将它们传递回 JavaScript 环境时。

napi_close_escapable_handle_scopeopen in new window

当然,我会努力以通俗易懂的方式来解释这个概念。

在 Node.js 中,napi_close_escapable_handle_scope是一个函数,它属于 N-API(Node.js API)的一部分,这是一个用于构建本地插件的 API。为了理解napi_close_escapable_handle_scope,我们首先需要理解几个关键概念:句柄(handle),句柄作用域(handle scope)和可逃逸的句柄作用域(escapable handle scope)。

句柄 (Handle): 在 Node.js 的上下文中,句柄是一个引用,指向 JavaScript 值或对象。由于 Node.js 内部使用 V8 引擎来管理 JavaScript 代码,所以句柄实际上是对 V8 引擎中对象的引用。

句柄作用域 (Handle Scope): 为了管理内存,V8 引擎使用了一个概念叫做句柄作用域。一个句柄作用域可以包含很多句柄。当你在 C++扩展中创建 JavaScript 值时,这些值会被放入当前的句柄作用域。当该作用域结束时,如果这些 JavaScript 值没有被其他部分的代码引用,它们就会被垃圾收集器清除掉。这有助于防止内存泄漏。

可逃逸的句柄作用域 (Escapable Handle Scope): 这是一种特殊的句柄作用域。通常情况下,在句柄作用域内创建的值不能“逃逸”到外面的作用域,但在可逃逸的句柄作用域内,你可以指定某个值“逃逸”,这意味着即使这个作用域结束了,该值也不会被清理,而是可以继续被外部的代码所使用。

现在让我们看看napi_close_escapable_handle_scope

napi_status napi_close_escapable_handle_scope(napi_env env,
                                              napi_escapable_handle_scope scope);

这个函数的作用是关闭一个可逃逸的句柄作用域。当你完成了在可逃逸的句柄作用域内的工作,并且已经标记了想要逃逸的值(如果有的话),你就应该调用这个函数来关闭作用域。

例子: 假设你正在编写一个接收一个 JavaScript 数组,并返回数组中第一个元素的扩展。在这种情况下,你可能会创建一个可逃逸的句柄作用域,获取数组的第一个元素,并将其标记为逃逸。然后使用napi_close_escapable_handle_scope来结束作用域,同时保持对那个元素的引用。

##include `<`node_api.h>

// 假设 env 是有效的 napi_env
napi_status status;
napi_escapable_handle_scope scope;

// 创建可逃逸的句柄作用域
status = napi_open_escapable_handle_scope(env, &scope);

// ... 假设你已经获取了数组的第一个元素 ...

// 将你希望逃逸的值标记为逃逸,确保它在作用域之外仍然有效
napi_value escaped_value;
status = napi_escape_handle(env, scope, array_element, &escaped_value);

// 关闭可逃逸的句柄作用域
status = napi_close_escapable_handle_scope(env, scope);

// 此时,escaped_value 依然有效,可以返回给 JavaScript

在这段代码中:

  • napi_open_escapable_handle_scope 创建了一个新的可逃逸的句柄作用域。
  • napi_escape_handle 标记了一个值使其能从作用域中逃逸。
  • napi_close_escapable_handle_scope 结束了这个作用域,但允许标记为逃逸的值在作用域之外生存。

请注意,这里的status变量用于检查每个 N-API 调用是否成功。在真正的代码中,你应该总是检查status并相应地处理错误。

希望这个解释有助于你理解napi_close_escapable_handle_scope函数及其在 Node.js 本地模块开发中的作用。

napi_escape_handleopen in new window

好的,让我们来详细了解一下 Node.js 中的 napi_escape_handle

首先,napi_escape_handle 是一个属于 Node.js N-API(原生 API)的函数。N-API 是 Node.js 提供的一个 C 语言的 API 层,允许你使用 C 或者 C++编写扩展模块。这些扩展模块可以直接调用低级别的 API,与 V8 引擎(Node.js 运行时的核心)进行交互,而无需关心 Node.js 内部代码的具体实现细节。

在理解napi_escape_handle之前,我们需要知道什么是"handle scope"。在 Node.js 中,为了有效地管理 JavaScript 对象的内存,V8 使用了一种称作垃圾回收(GC)的机制。为了配合这个机制,V8 引入了所谓的"handle scope":当你在 C/C++扩展中创建一个新的 JavaScript 对象时,它会被自动放入当前的 handle scope。当这个 scope 结束时,如果这个对象没有别的引用,它就可以被垃圾回收器清理掉。这样可以帮助防止内存泄漏。

然而,有时你可能希望一个 JavaScript 对象能够“逃逸”出当前的 handle scope,使其不受当前 scope 结束时垃圾回收的影响。这就是napi_escape_handle函数的作用。

举个实际的例子: 想象你正在编写一个 Node.js 扩展,该扩展创建了一个 JavaScript 对象,并希望在未来某个时候,即便是在当前的 handle scope 已经结束后,也能再次访问到这个对象。你可以使用napi_escape_handle来保证这个对象在当前 scope 外仍然可用。

代码示例可能类似于以下情况:

##include `<`node_api.h>

// 假设env是当前的环境标识,scope是你当前的handle scope
napi_handle_scope scope;
napi_open_handle_scope(env, &scope);

// 创建一个新的JavaScript对象
napi_value js_object;
napi_create_object(env, &js_object); // 直接操作V8引擎创建对象

// 假设你想让这个对象在scope外面也能访问到
napi_escapable_handle_scope escapable_scope;
napi_open_escapable_handle_scope(env, &escapable_scope);

// 通过napi_escape_handle让js_object逃逸出当前的handle scope
napi_value escaped_js_object;
napi_escape_handle(env, escapable_scope, js_object, &escaped_js_object);

// 现在即使关闭scope,escaped_js_object也不会被清除
napi_close_escapable_handle_scope(env, escapable_scope);
napi_close_handle_scope(env, scope);

// 你现在可以在任何时候安全地使用escaped_js_object,因为它已经逃逸出了scope

在上述代码中,napi_open_handle_scopenapi_open_escapable_handle_scope 用来分别打开普通的和可逃逸的 handle scopes。napi_escape_handle 则用于将js_objectescapable_scope中逃逸到外面,让它可以在escapable_scopescope关闭后继续存在。这样做之后,escaped_js_object可以在其他地方被安全地使用,而不必担心它会被垃圾回收掉。

总结一下,napi_escape_handle是一个高级特性,主要用于在编写 C/C++扩展时,确保 JavaScript 对象可以在它们被创建的 handle scope 外部生存。这对于管理复杂的对象生命周期十分有用,尤其是在异步操作和回调函数中。

References to values with a lifespan longer than that of the native methodopen in new window

在 Node.js 中,N-API 是一个用于构建原生插件的 API。原生插件是指直接使用 C 或 C++ 语言编写的模块,这些模块可以通过 Node.js 调用。这样做的理由通常是为了提高性能,或者是为了能使用某些只有在低级语言中才存在的功能。

在 JavaScript 中,当你创建一个对象或者函数,并且不再有任何引用指向它时,垃圾回收器将会自动释放该对象占用的内存。但在 C/C++ 中,管理内存的责任在于开发者,他们需要显式地分配和释放内存。

引用与寿命

当我们在 N-API 插件中处理 JavaScript 值时,可能会遇到一种情况:我们希望在原生代码中保持对 JavaScript 值的引用,即使当前的原生方法结束执行后也是如此。这是因为原生方法执行完毕后,按照 JavaScript 的垃圾回收机制,如果没有其他引用指向这个值,它可能就会被回收掉。

为了解决这个问题,N-API 提供了一种机制,允许我们在原生代码中创建对 JavaScript 值的强引用,这些引用保证了即使原生方法执行完毕,JavaScript 值也不会被垃圾回收器回收。

实际运用示例

假设你正在编写一个原生插件来处理图片。你在 JavaScript 端有一个图片的引用,而你想在后台进行一些长时间运行的图像处理工作。

##include `<`node_api.h>

// 假设这是一个将被长时间运行的图像处理函数
void ProcessImageInBackground(napi_env env, void* data) {
    // ...
    // 这里我们执行一些耗时的图像处理操作
    // ...
}

napi_value StartProcessingImage(napi_env env, napi_callback_info info) {
    napi_status status;

    // 获取传递给该函数的 JavaScript 图片对象
    size_t argc = 1;
    napi_value args[1];
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 创建一个对该 JavaScript 图片对象的强引用
    napi_ref image_ref;
    status = napi_create_reference(env, args[0], 1, &image_ref);

    // 将图像处理任务放入后台线程
    napi_value result;
    status = napi_create_async_work(env, ... , ProcessImageInBackground, ... , &result);

    // 启动异步工作
    status = napi_queue_async_work(env, result);

    return result;
}

在上面的伪代码中,StartProcessingImage 函数被调用时会启动一个图像处理的异步任务。由于这项任务将在后台线程中执行,我们需要确保所处理的 JavaScript 图片对象在整个处理过程中都是活跃的,不会被垃圾回收。为此,我们通过 napi_create_reference 创建了一个强引用。这个强引用将保留图片对象,直到我们主动删除这个引用,或者减少引用计数到零。

简单来说,这个特性让我们可以在原生插件中安全地持有 JavaScript 对象的引用,确保即使它们不再被 JS 代码直接引用,它们也不会在我们还需要它们的时候被垃圾回收。这对于实现复杂的异步操作和资源管理十分重要。

napi_create_referenceopen in new window

Node.js 中的 napi_create_reference 函数是一个 N-API 调用,它允许你在原生模块中创建一个对 JavaScript 对象的引用。这个函数主要用于管理原生代码与 JavaScript 代码之间对象的生命周期。

在详细解释这个函数之前,先了解一下几个关键概念:

  1. N-API(Node API):这是 Node.js 提供的一个稳定的 API 层,它允许你使用 C 或 C++ 编写扩展,而不必担心随 Node.js 版本变化导致的扩展不兼容问题。

  2. 引用(Reference):在编程中,引用通常是指向另一个对象的指针或者连接。在这种情况下,我们谈论的是指向 JavaScript 对象的引用。

  3. 垃圾回收(Garbage Collection,GC):JavaScript 有自动内存管理机制,当对象不再被需要时,垃圾回收器会自动释放它们占用的内存。但是,在原生模块中,我们可能需要确保某个对象在原生代码执行期间不会被 GC 清除。

现在来看看 napi_create_reference 函数的用法和目的:

napi_status napi_create_reference(napi_env env,
                                  napi_value value,
                                  uint32_t initial_refcount,
                                  napi_ref* result);
  • env:一个表示当前环境的句柄,在任何 N-API 调用中都需要。
  • value:想要创建引用的 JavaScript 对象。
  • initial_refcount:初始引用计数。如果你设置为 0,那么这个对象可以被垃圾回收;如果设置大于 0,那么对象会保持活跃状态直到引用计数降至 0。
  • result:这将输出一个 napi_ref 句柄,代表了创建的引用。

实际运用的例子:

假设你正在编写一个 Node.js 的原生扩展,该扩展与某硬件设备进行交互,并且当硬件设备发出特定事件时,它应该调用一个 JavaScript 回调函数。你希望这个 JavaScript 回调函数在原生模块存活期间一直有效,即使它在 JavaScript 代码中已经没有其他引用了。这就是 napi_create_reference 发挥作用的地方。

##include `<`node_api.h>

// 假设有个硬件事件监听器的结构体
typedef struct {
    void (*on_event)(void); // 当硬件事件发生时的回调指针
} HardwareEventListener;

// 这是在 JavaScript 调用原生模块来注册回调时的函数
napi_value RegisterCallback(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    napi_ref callbackRef;
    // 创建对回调函数的引用,并将初始引用计数设置为 1,确保它不会被垃圾回收
    napi_create_reference(env, args[0], 1, &callbackRef);

    // 存储回调引用以便稍后使用...
    // ...

    return NULL;
}

// 当硬件事件发生时,这个函数将被调用
void OnHardwareEvent() {
    // 使用之前存储的 `callbackRef` 来获取 JavaScript 回调函数并调用它
    napi_value callback;
    napi_get_reference_value(env, callbackRef, &callback);

    napi_value global;
    napi_get_global(env, &global);

    // 调用 JavaScript 回调函数
    napi_call_function(env, global, callback, 0, NULL, NULL);
}

// 注册该事件监听器
void SetupHardwareListener(HardwareEventListener *listener) {
    listener->on_event = OnHardwareEvent;
}

在这个例子中,我们创建了一个 napi_ref 引用来确保即使在 JavaScript 中没有其他引用,回调函数仍然不会被垃圾回收。这样,每当硬件事件发生时,我们都可以安全地调用这个 JavaScript 回调函数。

napi_delete_referenceopen in new window

当然,我很乐意帮助你了解 napi_delete_reference 函数。

首先,我们需要知道 N-API 是 Node.js 提供的一个用于构建原生插件的 API 接口。原生插件允许开发者使用 C 或者 C++ 等语言来编写可以直接运行在操作系统级别的代码,这些代码可以被 Node.js 项目调用。通常这是为了实现一些 JavaScript 难以实现或者性能不佳的底层操作。

在 N-API 中,napi_create_referencenapi_delete_reference 这两个函数与 “引用” 相关。在这里,“引用” 指的是从原生代码(C/C++)持有对 JavaScript 对象的引用。当你在原生模块中创建了一个引用后,即使 JavaScript 代码已经没有任何变量指向该对象,垃圾回收器也不会释放它,因为它知道原生代码仍然需要它。

现在来看 napi_delete_reference

  • 函数目的:删除先前创建的对 JavaScript 值的强引用,通常是因为原生代码不再需要引用该值。删除引用后,如果没有其他活动引用指向该 JavaScript 值,则该值可能会在未来的垃圾回收过程中被清除。

  • 基本用法

    napi_status napi_delete_reference(napi_env env, napi_ref ref);
    

    env 参数是代表 N-API 调用环境的句柄,而 ref 是之前使用 napi_create_reference 创建的引用。

  • 例子:假设我们有一个原生模块,其中包含一个全局 JavaScript 函数的引用,一旦我们确定不再需要这个函数,就应该删除引用,以避免内存泄漏。

    // 假设在某处已经创建了一个引用
    napi_ref my_function_ref; // 这是一个全局变量
    
    // ...等到不再需要这个函数时...
    
    // 删除引用,允许 JavaScript 垃圾回收器释放函数
    napi_status status = napi_delete_reference(env, my_function_ref);
    if (status != napi_ok) {
      // 处理错误情况
    }
    
    // 清空引用变量,表示它不再指向任何内容
    my_function_ref = NULL;
    

请注意,在实际的原生模块开发中,正确管理引用至关重要,以确保不会产生内存泄露。删除引用通常是在模块卸载时、对象不再需要时或者模块的生命周期结束时进行。

napi_reference_refopen in new window

好的,让我们聊聊 Node.js 中的 napi_reference_ref 这个功能。

首先,为了理解 napi_reference_ref,你需要知道一些关于 Node.js 和 N-API 的背景。

N-API 是 Node.js 的一个 C API,它允许本地插件(用 C 或 C++编写的代码)与 Node.js 的 JavaScript 引擎进行交互。这意味着开发人员可以使用其他编程语言编写能够与 JavaScript 代码无缝集成的模块。

在 JavaScript 中,有一个叫做垃圾回收(Garbage Collection,GC)的机制。这个机制会自动释放不再被程序使用的内存。但是,当你在 JavaScript 中使用由 N-API 创建的本地资源时,默认情况下,垃圾回收器不知道如何处理这些资源。因此,如果不适当地管理这些资源,就可能出现内存泄露。

现在来到 napi_reference_ref。这个函数与管理 N-API 中的对象引用计数有关。当你在本地代码中创建一个对 JavaScript 对象的引用,你需要确保即使 JavaScript 的垃圾回收想要清理这个对象,这个对象也不会被销毁,直到你的本地代码完成使用它。通过增加引用计数,你实际上是在告诉垃圾回收器:“嘿,我还在用这个对象,所以请不要删除它。”

具体来说,napi_reference_ref函数会增加一个引用的引用计数。每次调用这个函数,关联的对象就更难被垃圾回收器回收,直到相应数量的 napi_reference_unref 被调用来减少引用计数。

举个例子:

假设你正在编写一个本地插件,该插件保存了一个 JavaScript 对象的引用,以便随后使用。你会这样使用 napi_reference_ref

napi_status status;
napi_ref ref;

// 假设 `env` 是当前的环境句柄, `object` 是已经创建的N-API中的对象句柄
status = napi_create_reference(env, object, 1, &ref);
// 检查状态是否正常...

// 现在,你想要增加引用的引用计数,确保对象不会被垃圾收集器回收
int32_t ref_count;
status = napi_reference_ref(env, ref, &ref_count);
// 检查状态是否正常...

// 做一些工作...
// ...

// 如果你完成了对该对象的使用,可以减少引用计数
status = napi_reference_unref(env, ref, &ref_count);
// 检查状态是否正常...

// 当引用计数减少至0时,对象可以被垃圾回收器回收
if (ref_count == 0) {
    status = napi_delete_reference(env, ref);
    // 检查状态是否正常...
}

以上代码段演示了如何创建一个引用(napi_create_reference),增加引用计数 (napi_reference_ref),减少引用计数 (napi_reference_unref),并在不再需要时删除引用 (napi_delete_reference)。

简而言之,napi_reference_ref 在 N-API 中是用来帮助你管理本地代码中 JavaScript 对象的生命周期,以避免在本地插件与 JavaScript 之间的互操作过程中出现内存问题。

napi_reference_unrefopen in new window

好的,首先让我简单解释一下 Node.js 和 N-API 是什么,然后再详细讲解 napi_reference_unref 这个函数。

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的平台,允许你使用 JavaScript 来编写服务器端代码。N-API 则是 Node.js 提供的一套 C 语言 API,它允许本地插件(通常用 C 或 C++编写)与 JavaScript 代码交互。这意味着开发者可以在 JavaScript 中调用 C/C++ 代码,或在 C/C++ 代码中操作 JavaScript 对象。

现在,谈论 napi_reference_unref 函数。在 N-API 中,为了控制 JavaScript 对象的生命周期,你可以创建一个引用(reference)来保持对象活跃,防止它被垃圾回收机制回收。有时候你需要减少对这个对象的引用计数,说明你对这个对象的需求降低了,这就是 napi_reference_unref 函数的作用。

实际上,每当你用 napi_create_reference 创建对一个对象的新引用时,引用计数会增加。如果你想"解除"这个引用,你可以使用 napi_reference_unref 来减少引用计数。当引用计数降到 0 时,如果没有其他引用保持这个对象活跃,那么这个对象可能会在将来的某个时间点被垃圾回收。

这里是 napi_reference_unref 函数的签名:

napi_status napi_reference_unref(napi_env env, napi_ref ref, uint32_t* result);
  • env 是一个表示 N-API 环境的句柄。
  • ref 是你之前通过 napi_create_reference 创建的引用。
  • result 是一个输出参数,当函数执行后,它会保存新的引用计数值。

现在举一个简单的例子:

假设你编写了一个原生模块,该模块在初始化时创建了对一个特殊 JavaScript 对象的引用,并且希望在模块工作期间阻止该对象被垃圾回收。

// 假设 obj 是一个你已经获得的 napi_value 表示的 JS 对象
napi_ref my_ref;
napi_status status = napi_create_reference(env, obj, 1, &my_ref);
// 检查状态是否 OK,然后你的引用就被创建了

现在,某个时间点你决定不再需要这个引用,你可以像这样减少引用计数:

uint32_t ref_count;
status = napi_reference_unref(env, my_ref, &ref_count);
// 检查状态,如果 OK,ref_count 将包含减少后的引用计数

如果以后再也不需要这个对象,你可以完全删除这个引用:

if (ref_count == 0) {
    status = napi_delete_reference(env, my_ref);
    // 检查状态,确保引用被正确删除
}

理解 napi_reference_unref 及其相关函数对于编写健壮且内存管理良好的原生模块是很重要的。记住,错误的引用计数管理可能导致内存泄漏或提早垃圾回收对象,这可能导致程序崩溃或不可预测的行为。

napi_get_reference_valueopen in new window

Node.js 中的 N-API 是一个用于构建本地插件的 API 层,它提供了从 JavaScript 代码中调用 C 或 C++ 函数的功能。使用 N-API 可以创建可以由 Node.js 调用的本机模块,这样可以提升性能或者允许你使用系统级别的特性。

napi_get_reference_value 是 N-API 提供的一种函数,它用来从一个已经创建的引用中检索 JavaScript 值。在 Node.js 的本地插件开发中,我们时常需要保持对某些 JavaScript 对象的引用,以便后续使用或防止这些对象被垃圾回收器回收。为此,我们使用 N-API 创建引用,并通过 napi_get_reference_value 函数来获取这个引用所指向的实际 JavaScript 值。

下面我将解释 napi_get_reference_value 的基本使用方法,并给出一个简单的例子。

使用步骤

  1. 创建引用:首先,你需要有一个引用 (napi_ref) 指向一个 JavaScript 对象。这通常是通过 napi_create_reference 完成的。
  2. 获取引用值:之后,你可以使用 napi_get_reference_value 来获取引用指向的那个 JavaScript 值。

函数签名

napi_status napi_get_reference_value(napi_env env,
                                     napi_ref ref,
                                     napi_value* result);
  • env:当前环境的句柄,它代表了 Node.js 运行时的上下文,用于处理所有 N-API 调用。
  • ref:要获取其值的引用。
  • result:指向 napi_value 的指针,该函数会将引用的 JavaScript 值赋给这个指针。

函数返回值

  • 返回类型为 napi_status,它表示函数调用的状态。如果成功,会返回 napi_ok;如果失败,会返回错误代码。

示例

假设我们想要创建一个本地模块,该模块保持对某个 JavaScript 对象的引用,并且希望在未来某个时间点再次获取这个对象:

##include `<`node_api.h>

// 假定有一个全局变量保存引用
napi_ref global_ref;

// 创建引用的函数
napi_value CreateReference(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value argv[1];
  size_t argc = 1;

  // 获取传入的JavaScript对象
  status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "Failed to parse arguments");
  }

  // 创建一个弱引用,引用计数设置为1
  status = napi_create_reference(env, argv[0], 1, &global_ref);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "Failed to create reference");
  }

  return argv[0];
}

// 获取引用对象的函数
napi_value GetReferenceValue(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value result;

  // 使用global_ref获取JavaScript对象
  status = napi_get_reference_value(env, global_ref, &result);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "Failed to get reference value");
  }

  // 返回JavaScript层面的对象
  return result;
}

// 初始化模块...

在这个例子中,我们定义了两个函数:CreateReferenceGetReferenceValueCreateReference 接收一个 JavaScript 对象,并创建一个引用。然后,我们可以在任意时间点,通过 GetReferenceValue 函数来获取这个引用所指向的原始 JavaScript 对象。

注意事项

  • 使用引用时需要注意适当管理它们的生命周期,避免内存泄露。
  • 引用可以是强引用或弱引用,区别在于它是否阻止垃圾回收器回收其指向的 JavaScript 对象。

以上就是 napi_get_reference_value 在 Node.js N-API 中的基本介绍和一个简单的示例。希望这有助于你理解如何在本地插件中处理 JavaScript 对象的引用。

Cleanup on exit of the current Node.js environmentopen in new window

Node.js 中的"N-API"是一个用于构建本地插件(native addons)的 API。本地插件是一些用 C, C++等语言编写的模块,它们可以直接与 Node.js 的 JavaScript 运行时进行交互。使用本地插件,你可以执行一些需要更高性能或者需要直接访问系统资源的任务。

在 Node.js v21.7.1 版本中,“Cleanup on exit of the current Node.js environment”指的是当 Node.js 进程退出时,相关的 N-API 资源会被清理掉。这意味着如果你的 Node.js 应用程序正在使用本地插件,并且创建了一些通过 N-API 创建的对象,那么当 Node.js 进程结束时(例如,你关闭了服务器),这些对象所占用的资源会自动被释放,防止内存泄漏。

具体来说,这个特性涉及到如何处理各种回调和钩子(hooks),以确保当 Node.js 环境终止时,任何未完成的任务都能正确地清理干净,从而避免内存泄露和其他潜在问题。这通常会在如下情况中发生:

  • 当 Node.js 由于异常错误而退出时。
  • 当 Node.js 正常关闭时,比如调用process.exit()

实际运用的例子可能包括:

  1. 数据库连接插件: 如果你使用一个本地插件来管理数据库连接,那么在 Node.js 退出时,所有的数据库连接都会被自动关闭,防止出现连接没有正确结束的情况。

  2. 文件操作插件: 假设你有一个本地插件用于执行文件 I/O 操作,在 Node.js 结束时,所有打开的文件描述符会被关闭,确保没有文件被意外留在打开状态中。

  3. 硬件接口插件: 如果你的 Node.js 应用程序与某些硬件设备进行通信,比如通过串口与传感器交互,那么在退出时,相关的硬件资源也会被适当释放。

要点是,N-API 提供了一组稳定的 APIs,使得本地插件的开发者不需要对每一个 Node.js 的新版本都做适配,同时确保了 Node.js 环境退出时资源能够得到正确的清理。这样的设计既方便了开发者,也提高了程序的稳定性和安全性。

napi_add_env_cleanup_hookopen in new window

好的,首先来简单介绍一下 Node.js 和 N-API 是什么,然后我会详细解释 napi_add_env_cleanup_hook 这个函数及其应用。

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行环境,它允许我们在服务器上运行 JavaScript 代码。Node.js 的特点是异步和非阻塞 IO,这使得它非常适合处理大量并发连接,例如在网络服务器或与数据库的交互中。

N-API 是 Node.js 的一个稳定的 API 层,用于构建原生插件。原生插件是用 C 或 C++ 写的模块,可以直接调用系统级别的 API,比如操作文件系统或者创建网络连接。N-API 的作用是提供一组稳定、版本无关的接口,允许原生插件代码与 Node.js 进行交互,而不必担心底层 JavaScript 引擎的变化。

现在,让我们来看看 napi_add_env_cleanup_hook 这个函数。

napi_add_env_cleanup_hook 函数用于注册一个回调函数,这个回调函数将在 Node.js 环境即将销毁时被调用。这通常发生在 Node.js 应用程序即将退出时,或者当某个特定的 Node.js 工作线程(例如在 worker_threads 模块中)即将结束时。

为什么需要这个功能呢?

当你创建原生插件时,可能会分配一些资源,例如内存、文件描述符或其他操作系统资源。如果这些资源在插件生命周期结束时没有正确地清理,可能会导致资源泄露。使用 napi_add_env_cleanup_hook 可以确保当 Node.js 环境结束时,你有机会释放这些资源,从而避免资源泄露。

下面是 napi_add_env_cleanup_hook 的基本用法示例:

##include `<`node_api.h>

// 这是你希望在环境清理时执行的函数
void cleanup(void* arg) {
    // 释放资源,arg 是在注册回调时传递的任意数据
}

// 初始化函数,当你的插件被加载时执行
napi_value Init(napi_env env, napi_value exports) {
    // ...

    // 注册清理钩子
    napi_add_env_cleanup_hook(env, cleanup, nullptr);

    // ...
    return exports;
}

// 使用 NODE_API_MODULE 宏注册你的插件
NODE_API_MODULE(your_module_name, Init)

在此代码中:

  1. cleanup 函数是在 Node.js 环境终止前需要执行的清理逻辑。
  2. Init 函数是模块初始化时调用的入口点,在这里注册了清理钩子。
  3. napi_add_env_cleanup_hookcleanup 函数注册为清理钩子,而第三个参数是可选的,可以传递给 cleanup 函数的数据。

实际应用举例:

  • 如果你的插件打开了文件进行读写,你需要确保在退出前关闭这些文件。
  • 如果你的插件启动了一个新的线程或进程,并在背后运行,你需要确保在清理钩子中停止并清理这些线程或进程。
  • 如果你的插件分配了内存,可能需要在清理钩子中释放这些内存。

总结一下,使用 napi_add_env_cleanup_hook 可以确保当 Node.js 环境结束时,原生插件有机会进行适当的资源回收和清理工作,这对于编写高效、不泄露资源的原生插件至关重要。

napi_remove_env_cleanup_hookopen in new window

Node.js 中的 N-API 是一个用于构建本机插件的 API。这个 API 设计得相对底层,提供了更直接地与 JavaScript 运行时和 V8 引擎交互的能力。N-API 旨在不仅跨 Node.js 版本提供稳定性,还降低向下兼容性问题,因此开发者可以编写一次代码,在多个版本的 Node.js 上运行。

napi_remove_env_cleanup_hook 是 N-API 中的一个函数,它的作用是移除之前使用 napi_add_env_cleanup_hook 函数注册的环境清理钩子(cleanup hook)。所谓“环境清理钩子”是指当 Node.js 环境正在销毁时将要执行的函数,它们通常用来释放资源,比如关闭文件描述符、解除内存分配等。

现在,让我们以通俗易懂的方式进行解释:

想象你参加了一个聚会,在聚会结束之前,你说:“聚会结束时候我会帮忙收拾桌子。”你的这个承诺就类似于一个“清理钩子”,聚会主办方会记住这个承诺,并在聚会结束时调用它,即告诉你去执行收拾桌子的工作。

但是如果在聚会结束之前,你不得不提前离开,你可能需要告知主办方:“嘿,我得走了,所以请别指望我来收拾桌子。”这样,主办方就会从他们的待办列表中移除你的那项任务。在 Node.js 中,使用 napi_remove_env_cleanup_hook 就是执行这个“通知取消”的动作。

实际使用的例子:

假设你编写了一个 Node.js 的本地插件,它打开了一个文件,并且在 Node.js 环境清理时需要确保这个文件被正确关闭。你可能首先会用 napi_add_env_cleanup_hook 添加一个清理钩子来关闭这个文件。

// 假设有这样一个关闭文件的函数
void close_file(void* arg) {
  // ... 关闭文件的代码 ...
}

// 注册清理钩子
napi_status status = napi_add_env_cleanup_hook(env, close_file, nullptr);
if (status != napi_ok) {
  // 处理错误情况
}

现在设想,随着代码的运行,出于某种原因,你的插件在 Node.js 环境结束之前已经关闭了文件,并且你确定不再需要在环境清理时执行这个钩子。这时,你就可以使用 napi_remove_env_cleanup_hook 来移除之前设置的清理钩子。

// 移除清理钩子
status = napi_remove_env_cleanup_hook(env, close_file, nullptr);
if (status != napi_ok) {
  // 处理错误情况
}

总结起来,napi_remove_env_cleanup_hook 的作用就是为了撤销那些不再需要在 Node.js 环境结束时执行的任务,以确保资源得到正确的管理和释放。

napi_add_async_cleanup_hookopen in new window

Node.js 中的 N-API 是一个 C 语言的 API 层,它允许你编写原生插件。原生插件是一些用 C 或 C++等编程语言编写的模块,可以直接调用底层操作系统的 API 或者执行一些 JavaScript 引擎不能直接执行的计算密集型任务。

在 Node.js 中,有时候我们会启动一些异步操作,比如读取文件、发送网络请求等。这些操作通常由 Node.js 后台的线程池处理。但是如果你正在编写一个原生模块,可能需要在自己的代码中创建额外的资源,比如动态分配内存、打开文件句柄或数据库连接等。当 Node.js 的进程退出时,你需要确保清理这些资源,以避免内存泄露或其他问题。

napi_add_async_cleanup_hook函数正是为了解决这个问题而存在。它可以注册一个回调函数,当 Node.js 的异步操作即将完成并且进程准备退出时,这个回调函数会被调用。这样,你就可以在回调函数中做一些清理工作。

使用napi_add_async_cleanup_hook的具体步骤如下:

  1. 定义清理函数: 创建一个函数,这个函数包含了你要执行的清理逻辑。

  2. 注册清理钩子: 在你的原生模块的合适位置调用napi_add_async_cleanup_hook,将你的清理函数和任何需要传递给它的数据作为参数提供给这个 API。

  3. 执行清理: 当 Node.js 准备退出或者模块被卸载时,该清理函数会被调用。

下面是一个简化的例子来说明这个过程:

首先,你将定义一个清理函数,例如用于关闭文件描述符:

void cleanup_file_descriptor(void* arg) {
    int fd = *(int*)arg;
    close(fd); // 假设这是一个关闭文件描述符的标准库函数
}

然后,在你的模块初始化或者创建资源时,你将注册这个清理钩子:

napi_env env; // N-API环境变量,通常从函数参数获得
int* fd_ptr = malloc(sizeof(int)); // 动态分配内存存放文件描述符
// ... 你的代码打开文件,并将结果存储在fd_ptr指向的内存中

// 注册清理钩子
napi_status status = napi_add_async_cleanup_hook(env, cleanup_file_descriptor, fd_ptr, NULL);

if (status != napi_ok) {
    // 处理错误情况
}

现在,无论何时你的模块被卸载或者 Node.js 进程退出,cleanup_file_descriptor都会被调用来关闭文件和释放资源。

请注意,你需要处理好所有可能的边界情况,比如清理函数被调用时资源已经清理过了,或者在异步操作尚未完成时进程退出等情况。正确地管理资源是编写可靠和高效原生模块的关键部分。

napi_remove_async_cleanup_hookopen in new window

好的,让我来解释一下 Node.js 中的 napi_remove_async_cleanup_hook 是什么以及如何使用它。

首先,我们需要了解 N-API 是什么。N-API(Node.js API)是一个内置于 Node.js 的 C API 层,它让你可以用 C 或 C++ 编写扩展模块,并保证对 Node.js 不同版本的兼容性。简单来说,它允许开发者编写可跨 Node.js 版本运行的原生插件。

现在,谈论 napi_remove_async_cleanup_hook 这个函数,它是 N-API 的一部分。此函数用于移除之前通过 napi_add_async_cleanup_hook 添加的异步清理钩子(hook)。"异步清理钩子"是一种在异步操作完成时或 Node.js 环境被销毁时调用的函数,主要用于资源的清理工作。

为什么需要这样的机制?想象这样一个情况:你创建了一个原生插件,里面有些异步操作,比如文件读写、网络请求等。这些异步操作可能会使用到一些需要手动释放的资源,如动态分配的内存、打开的文件句柄等。如果你的插件在没处理完这些操作就被卸载了,就需要确保这些资源被正确地释放,避免内存泄漏等问题。这就是添加异步清理钩子的目的。

当你不再需要这些清理钩子时,就可以用 napi_remove_async_cleanup_hook 来删除它们。这通常发生在你确定异步操作已经完全清理干净,或者你的模块正在被卸载并且你想显式地进行清理。

举个实际的例子:

假设你的原生扩展模块中有一个异步操作,这个操作在背后打开了一个文件,并进行了一些读写操作。你会使用 napi_add_async_cleanup_hook 来注册一个回调函数,在这个回调函数中关闭文件句柄,并释放任何分配的内存。

// 假设这是你的清理函数
void cleanup_file(void* arg) {
  // 这里我们关闭文件并释放内存
}

// 在你的异步操作中,你添加了清理钩子
napi_status status = napi_add_async_cleanup_hook(env, cleanup_file, NULL, &handle);

但是在某个时间点,你知道文件已经安全地关闭,相关资源也已经释放了,因此你不再需要清理钩子。这时你就可以调用 napi_remove_async_cleanup_hook 来移除它:

// 移除之前添加的清理钩子
napi_status remove_status = napi_remove_async_cleanup_hook(env, handle);

总结一下,napi_remove_async_cleanup_hook 是 N-API 提供的一种机制,让你能够管理和移除在原生模块中添加的资源清理回调,保证资源得到妥善处理。在实际使用中,它帮助你控制何时停止监听和执行清理工作,特别是在你确认资源已经被清理或者模块即将卸载时非常有用。

Finalization on the exit of the Node.js environmentopen in new window

Node.js 是一个运行在服务器端的 JavaScript 环境,它允许开发者使用 JavaScript 来编写服务器端代码。在 Node.js 中有一个名为 N-API 的接口,它允许原生模块(通常是用 C 或 C++ 写的扩展)与 JavaScript 代码进行交互。

在 N-API 中,“Finalization”一词指的是一个对象的清理过程,即当该对象不再需要时,将其占用的资源进行释放的过程。这在处理原生资源如文件句柄、网络连接或者内存分配时尤为重要,以避免出现资源泄露,即这些资源没有被适当释放,导致内存等资源的浪费。

在 Node.js v21.7.1 的文档中提到的“Finalization on the exit of the Node.js environment”特指 Node.js 应用程序退出时触发的清理动作。这意味着当你的 Node.js 程序结束运行时,N-API 会确保所有注册了 finalizer 的原生对象都得到妥善处理,即使它们还没有显式地通过垃圾回收机制被清理。

这个功能的重要性在于,它保证了即便在异常情况下,比如程序崩溃或是由于其他原因提前退出时,仍然能够释放掉所有的原生资源。

让我们举几个实际的例子来说明这个概念:

  1. 数据库连接: 假设你正在使用一个 Node.js 的数据库连接库来与数据库进行交互。每个数据库连接可能都会在系统中打开一个文件描述符或者维持一个网络套接字。当你的程序正常结束时,你可能会手动关闭这些连接。然而,如果程序非正常退出,比如由于一个未捕获的异常,那么没有执行清理代码的话,这些数据库连接可能就不会关闭。使用 N-API,并注册了正确的 finalizer,可以保证在 Node.js 环境退出时这些连接能够被关闭。

  2. 临时文件清理: 你的应用程序可能会创建临时文件来处理数据。在应用程序的正常工作流程中,这些临时文件在使用完之后会被删除。但是,如果程序意外退出,可能会留下这些没用的临时文件。通过在原生模块中设置 finalizer,无论程序正常还是异常退出,都能保证这些文件会在 Node.js 环境退出时被删除。

  3. 自定义资源管理: 如果你正在编写一个对外部硬件(例如 USB 设备)进行操作的原生模块,当 Node.js 环境退出时,你需要确保所有的硬件连接被正确断开,并且相关资源得到释放。设置 finalizer 可以帮助你在环境退出时执行必要的资源清理动作。

总结一下:“Finalization on the exit of the Node.js environment”是关于确保在 Node.js 环境退出时,所有的原生资源都能够被正确释放的机制。这是一个重要的功能,因为它防止了资源泄露,保护了系统的健康和稳定性。

Module registrationopen in new window

Node.js 中的 Module registration(模块注册)是一个关键的过程,用于告诉 Node.js 有哪些模块可用,以及如何在需要时加载和使用这些模块。Node.js 使用 CommonJS 规范来组织和使用模块,而 N-API 是 Node.js 提供的一套稳定的 API,允许原生插件(通常是 C 或 C++编写的模块)与 Node.js 代码无缝地交互。

在 Node.js v21.7.1 中,我们可以使用 N-API 来创建原生插件,并通过模块注册将这些插件暴露给 Node.js 环境。让我们逐步详细了解这个过程:

步骤 1: 创建原生模块源文件

首先,你需要用 C 或 C++编写你的原生模块代码。比如,假设你想创建一个简单的原生模块hello, 它只做一件事:返回"Hello, world!"字符串。

##include `<`node_api.h>

napi_value HelloMethod(napi_env env, napi_callback_info info) {
    napi_value greeting;
    napi_status status;

    // 创建一个JavaScript字符串 "Hello, world!"
    status = napi_create_string_utf8(env, "Hello, 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, NULL, 0, HelloMethod, NULL, &fn);
    if (status != napi_ok) return nullptr;

    // 将上面创建的函数设置为这个模块的属性 "hello"
    status = napi_set_named_property(env, exports, "hello", fn);
    if (status != napi_ok) return nullptr;

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

这段代码定义了一个函数HelloMethod,它会返回一个包含"Hello, world!"字符串的 JavaScript 值。然后在Init函数中,我们把HelloMethod作为模块的导出函数,使得当模块被加载时,可以通过require调用该函数。

步骤 2: 编译原生模块

要使用这个原生模块,它必须被编译成 Node.js 可以加载的格式(通常是.node文件)。这通常是通过工具像node-gyp来完成的。你需要一个binding.gyp文件来告诉node-gyp如何编译你的模块。

步骤 3: 加载和使用模块

编译完成后,你可以像使用普通的 JavaScript 模块一样在 Node.js 中使用你的原生模块。

const helloAddon = require("./build/Release/hello.node");

console.log(helloAddon.hello()); // 输出 "Hello, world!"

通过require加载原生模块后,你就可以调用模块中定义的函数,就像上面例子中的helloAddon.hello()

实际运用的例子

  • 性能优化:如果你的 Node.js 应用中有特别计算密集型的任务,你可能会选择用 C++来实现这些部分以提高性能。
  • 系统级操作:比如你需要直接和操作系统底层 API 交互,那么原生模块会非常有用。
  • 第三方库的集成:如果存在某些没有 JavaScript 版本但功能强大的 C/C++库,你可以通过编写原生插件来在 Node.js 中使用这些库。

Node.js 的原生模块注册机制为开发者提供了强大的功能扩展能力,但同时也涉及到更复杂的编程知识,可能需要对 C/C++及操作系统有较深的理解。

Working with JavaScript valuesopen in new window

Node.js 是一个让 JavaScript 可以在服务器端运行的平台。在 Node.js 中,你可以编写 JavaScript 来操控服务器的功能,比如文件系统、网络通信等。当我们在 Node.js 中使用 JavaScript 编写代码时,我们经常需要处理各种数据值,例如字符串、数字、对象等。

为了更好地在原生模块中(由 C 或 C++编写的模块)与 JavaScript 的数据进行交互,Node.js 提供了一个 N-API,这是一个用于构建原生插件的 API。N-API 提供了一系列的函数,使得原生代码能够创建和操作 JavaScript 中的值。

以下是一些基本的 JavaScript 值操作和对应的例子:

  1. 创建 JavaScript 赋值: 在原生模块中,你可能需要创建一个新的 JavaScript 字符串或者数字,并将它返回给 JavaScript 环境。例如:

    ##include `<`node_api.h>
    
    napi_value create_number(napi_env env) {
        napi_value num;
        napi_status status = napi_create_double(env, 123.456, &num);
        // 检查是否成功创建数字
        if (status == napi_ok) {
            return num; // 返回创建的数字
        } else {
            // 处理错误情况
        }
    }
    

    在上面的代码中,我们使用 napi_create_double 函数创建了一个 JavaScript 数字,并通过参数 env(表示当前的执行环境)和 num(用来存放创建的 JavaScript 数字)。

  2. 从 JavaScript 值中读取数据: 如果你想从 JavaScript 环境传递给原生模块的数据中读取信息,你可以使用 N-API 提供的函数来做到。例如,如果你要读取一个从 JavaScript 传入的数字:

    void read_number(napi_env env, napi_value js_value) {
        double value;
        napi_status status = napi_get_value_double(env, js_value, &value);
        // 检查是否成功读取数值
        if (status == napi_ok) {
            printf("Received number: %f\n", value);
        } else {
            // 处理错误情况
        }
    }
    

    在这个例子中,napi_get_value_double 函数用于获取 JavaScript 传入的数值并存储在变量 value 中。

  3. 操作 JavaScript 对象: JavaScript 中的对象是键值对的集合,在原生代码中,你也可以创建和修改这样的对象。例如,如果你要创建一个 JavaScript 对象并设置属性:

    napi_value create_object(napi_env env) {
        napi_value obj;
        napi_status status;
    
        // 创建一个新的空对象
        status = napi_create_object(env, &obj);
    
        // 添加一个属性“name”到对象中
        if (status == napi_ok) {
            napi_value name;
            status = napi_create_string_utf8(env, "Node.js", NAPI_AUTO_LENGTH, &name);
            if (status == napi_ok) {
                status = napi_set_named_property(env, obj, "name", name);
            }
        }
    
        // 检查整个过程是否成功
        if (status == napi_ok) {
            return obj; // 返回创建的对象
        } else {
            // 处理错误情况
        }
    }
    

    这段代码首先创建了一个新的 JavaScript 对象 obj,然后创建了一个 JavaScript 字符串 name,最后,它将 name 添加到 obj 对象作为一个名为 "name" 的属性。

通过以上示例,你可以看到 Node.js 中的 N-API 如何帮助我们在原生模块与 JavaScript 之间传递和操作数据。这使得 Node.js 可以被用于更加复杂和性能要求高的场景,因为原生模块通常比纯 JavaScript 代码执行得更快。

Enum typesopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。N-API 的目的是减少维护原生插件时 Node.js 版本更迭可能引发的问题,它提供了一套稳定的 API 集合,使得插件与 Node.js 版本之间更加解耦。

在 N-API 中的 Enum(枚举)类型是一种特殊的数据类型,它允许开发者定义一组命名的常量。使用枚举可以更清晰地表达代码的意图,并限制变量可以接受的值范围。

举个例子,让我们想象你正在编写一个原生插件来处理文件系统操作。在这种情况下,你可能需要处理不同类型的文件系统错误。通常,这些错误可以分成若干类别,例如 "权限错误"、"路径不存在" 和 "盘满" 等。使用枚举类型可以帮助你预先定义这些错误类别:

// 定义枚举类型
enum FileSystemError {
  PERMISSION_ERROR,
  PATH_NOT_FOUND,
  DISK_FULL
};

然后,在你的代码中,你可以使用这些枚举值来表示特定的错误:

// 使用枚举类型
FileSystemError errorType = PATH_NOT_FOUND;

if (errorType == PATH_NOT_FOUND) {
  // 处理路径不存在的错误
}

在 N-API 中,有很多内置的枚举类型,它们代表了 Node.js 原生层面上各种不同的值和状态。例如,当创建异步工作队列项目或处理回调时,你会用到这样的枚举类型。这些枚举值在 N-API 文档中都有详细的描述。它们通常用来指示如何处理 JavaScript 值和原生函数调用的结果。

假设你要创建一个异步函数,这个函数需要在完成时返回其执行的结果,那么你可能会使用 napi_status 枚举来传递操作的结果状态:

##include `<`node_api.h>

// 一个异步函数的示例,该函数在执行后会返回状态码
napi_value SomeAsyncFunction(napi_env env, napi_callback_info info) {
  napi_status status;

  // ...执行一些操作...

  // 跟据执行结果设置状态
  status = (执行成功) ? napi_ok : napi_generic_failure;

  // 返回状态码对应的JavaScript值
  napi_value result;
  napi_create_int32(env, (int32_t)status, &result);
  return result;
}

在这个例子中,napi_status 是 N-API 定义的一个枚举类型,其中包含了诸如 napi_ok, napi_invalid_arg, napi_object_expected 等等,用来指示函数调用的结果状态。在实际的异步函数中,你会根据操作的成功或失败,设置相应的枚举值,然后将这个值转换成 JavaScript 能够理解的形式,返回给调用者。这样的设计可以让你的原生插件与 JavaScript 代码之间有一个清晰的交互界面。

napi_key_collection_modeopen in new window

Node.js 中的 N-API (Native API) 是一个用于构建原生插件的 API,它让 JavaScript 代码能够和 C/C++代码交互,这样你就可以使用 C/C++来编写一些性能敏感或者需要直接与操作系统底层交互的部分,并且在 Node.js 环境中调用它们。

napi_key_collection_mode 是 N-API 中的一个枚举类型,用于设定如何收集对于 Native 对象的 property keys。在 JavaScript 中,对象是由键值对组成的,而当我们从 C/C++代码中创建或操作 JavaScript 对象时,可能需要考虑到垃圾回收(Garbage Collection,简称 GC)的问题。

所以,napi_key_collection_mode 有以下两种模式:

  1. napi_key_include_prototypes:这个模式意味着当收集对象的 keys 时,会包括该对象原型链上的 keys。
  2. napi_key_own_only:这个模式下,则只会收集对象自身的 keys,而不包括其原型链上的任何 keys。

举个例子,假设你在 C/C++插件中创建了一个 JavaScript 对象,并且想要获取它的属性键列表。如果你选择napi_key_include_prototypes模式,你得到的列表将包括对象本身的 keys 加上它继承的所有 keys(例如,来自它的原型或prototype属性)。如果你选择napi_key_own_only,那么你只会得到对象自己的 keys,不包含继承的。

在实际的应用场景中,这个功能可能被用在插件需要详尽地检查 JavaScript 对象的情况下。比如,如果你正在编写一个插件来序列化一个 JavaScript 对象,那么你可能需要根据是否希望包括原型链上的属性来选择合适的napi_key_collection_mode

这里是一个简化的示例代码片段(并非完整程序),它展示了如何在 C/C++中使用napi_key_collection_mode

##include `<`node_api.h>

// ... 省略其他必须的函数和错误处理 ...

napi_value GetOwnPropertyKeys(napi_env env, napi_callback_info info) {
    napi_status status;

    // 获取JavaScript传递的对象
    size_t argc = 1;
    napi_value args[1];
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查参数数量和类型等...

    // 创建一个数组来存放对象的keys
    napi_value result;
    status = napi_get_property_names(env, args[0], &result);

    // 设置key collection mode为仅包括对象自身的keys
    status = napi_set_property_names_enum(env, args[0], napi_key_own_only);

    // ... 还有更多的流程和错误处理 ...

    return result; // 返回keys数组
}

在上面的代码中,GetOwnPropertyKeys是一个函数,它试图从 Node.js 传入的对象中获取所有的自身属性 keys。我们首先获得了 JS 传递给我们的对象,然后使用napi_get_property_names获取属性 keys,并通过napi_set_property_names_enum设置我们想要的 key collection 模式。最终返回了一个包含所有自身属性 keys 的数组。

请注意,由于这个特性是 N-API 的一部分,它通常在相对底层的场景中使用,并且这里的代码只是为了解释napi_key_collection_mode是如何工作的,实际使用中需要考虑错误处理和内存管理等问题。

napi_key_filteropen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。一个原生插件是一个可以直接调用 C/C++代码的 JavaScript 模块,这使得开发者能够编写性能关键型操作,或是需要使用到系统底层功能的应用程序。

在 N-API 中有一个概念叫“属性键过滤”(Property Key Filtering),这涉及到在操作对象时筛选出哪些属性键(即对象的属性名)是我们感兴趣的。napi_key_filter是一个枚举类型,它定义了不同的筛选选项,让你能够更精准地控制哪些键被包含在某些特定 N-API 函数的操作中。

下面是一些napi_key_filter可能的选项:

  • napi_key_all_properties: 不进行过滤,包括所有的属性。
  • napi_key_own_only: 只包括对象自己的属性,不包括从原型链继承的属性。
  • napi_key_writable: 只包括可写的属性。
  • napi_key_enumerable: 只包括可枚举的属性。
  • napi_key_skip_strings: 跳过字符串键,只关注 Symbol 键。
  • napi_key_skip_symbols: 跳过 Symbol 键,只关注字符串键。

实际运用举例:

  1. 获取对象的所有自有属性:

    如果你想获取一个对象上所有属于该对象本身的属性(不包括那些通过原型链继承来的),你可以结合使用napi_key_own_only和一些相关的 N-API 函数。

  2. 枚举可写属性:

    你可能正构建一个原生插件,需要复制一个 JavaScript 对象的属性到 C++对象中去。但你只希望复制那些可写的属性。在这种情况下,你可以使用napi_key_writable作为过滤器。

  3. 避开 Symbols:

    JavaScript 中的 Symbol 是唯一的,不能被常规字符串键污染。如果你的应用逻辑需要跳过处理 Symbol 键,你可以使用napi_key_skip_symbols

示例代码片段(并非完整程序):

##include `<`node_api.h>

// 假设 env 和 object 是已经定义好的变量

// 获取对象的所有自有属性
napi_value result;
napi_status status = napi_get_property_names(env, object, napi_key_own_only, &result);
if (status == napi_ok) {
  // 使用 result 这里代表包含了所有自有属性的数组
}

// 此处的代码需要配合完整的N-API使用环境来工作

上面的代码示例仅用于说明如何在 C++中使用napi_key_own_only过滤器与 N-API 相结合进行操作。开发者需要提供完整的错误处理和环境设置代码以确保其正确运行。记住,这些接口是面向 C/C++开发者的,对于初学者来说可能需要较长时间去熟悉和理解。

napi_key_conversionopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行环境,它允许在服务器端运行 JavaScript 代码。N-API(Node API)是 Node.js 提供的一组 C API,使得本地插件(通常用 C 或 C++编写)可以不受特定版本 Node.js 影响地运行。

napi_key_conversion 并不是 Node.js v21.7.1 中特定的功能,而是指 N-API 当中与键转换相关的多个函数。这些函数被用来将 JavaScript 中的对象属性名(keys)转换为适合本地插件使用的格式,或反之。在实际应用中,这使得 JavaScript 代码和本地插件能够相互交换数据。

举几个实际例子来说明:

  1. 读取 JavaScript 对象属性:你可能在 C/C++ 插件中需要读取一个从 JavaScript 传入的对象的属性。使用 N-API 中的键转换函数,你可以将 JavaScript 的字符串属性名转换为一个 N-API 可以理解的表示形式,然后获取该属性的值。

  2. 设置 JavaScript 对象属性:相对地,如果你想在本地插件中创建或修改 JavaScript 对象的属性,你可以先将属性名转换为 N-API 的键表示,然后设置其值。

  3. 处理 JavaScript Map 或 Set:当 JavaScript 的 Map 或 Set 数据结构与本地插件交互时,你可能需要将 JavaScript 的值转换为适当的键,以便在 C/C++ 插件中使用这些集合。

请注意,由于我不能提供超出知识截止日期范围的具体信息,所述内容仅基于 Node.js 和 N-API 在该时间点之前的通用概念进行了解释。如需最新详情,请参考 Node.js 的官方文档。

napi_valuetypeopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。原生插件是可以直接与 Node.js 运行时进行交互的 C 或 C++代码模块,它们通常用于执行计算密集型任务或进行系统级操作,比如访问硬件。

napi_valuetype是 N-API 提供的枚举类型之一,用于表示 JavaScript 值在原生代码中的类型。在 JavaScript 中,各种数据类型(如数字、字符串、对象等)都可以通过 N-API 在原生代码里有对应的表示方法。了解一个 JavaScript 值的具体类型对于原生插件的编写是非常重要的,因为不同的类型在原生层面上可能需要不同的处理方式。

napi_valuetype 枚举

这个枚举包括了一系列预定义的值,每一个都代表了一个 JavaScript 值的类型。下面是一些例子:

  • napi_undefined: 表示 JavaScript 的undefined类型。
  • napi_null: 表示 JavaScript 的null类型。
  • napi_boolean: 表示 JavaScript 的布尔类型(truefalse)。
  • napi_number: 表示 JavaScript 的数值类型。
  • napi_string: 表示 JavaScript 的字符串类型。
  • napi_symbol: 表示 JavaScript 的 Symbol 类型。
  • napi_object: 表示 JavaScript 的对象类型。
  • napi_function: 表示 JavaScript 的函数类型。
  • napi_external: 表示一个外部类型,这通常用于表示一个 C/C++数据结构绑定到一个 JavaScript 对象。

实际运用的例子

假设我们正在编写一个原生插件,并且我们想要获取从 JavaScript 传递给我们的值的类型。以下是如何使用napi_valuetype来确定一个 JavaScript 值的类型:

##include `<`node_api.h>

// 假设我们已经有了一个napi_value类型的变量`value`代表一个JS传递过来的值

napi_valuetype valueType;
napi_status status = napi_typeof(env, value, &valueType);

if (status == napi_ok) {
  switch (valueType) {
    case napi_undefined:
      // 处理undefined值
      break;
    case napi_null:
      // 处理null值
      break;
    case napi_boolean:
      // 处理布尔值
      break;
    case napi_number:
      // 处理数值
      break;
    case napi_string:
      // 处理字符串
      break;
    case napi_symbol:
      // 处理Symbol
      break;
    case napi_object:
      // 处理对象
      break;
    case napi_function:
      // 处理函数
      break;
    case napi_external:
      // 处理外部绑定的数据结构
      break;
    default:
      // 未知类型
      break;
  }
} else {
  // 处理获取类型失败的情况
}

上述代码展示了如何使用napi_typeof来获取一个napi_value对应的类型,并且根据这个类型执行不同的逻辑处理。 这在原生插件开发中是很常见的场景,特别是当你需要处理从 JavaScript 传递过来的多种类型的参数时。

通过正确地识别和处理不同的 JavaScript 值类型,原生插件就能够更安全和有效地执行其任务,并且能够更好地与 JavaScript 代码进行交互。

napi_typedarray_typeopen in new window

好的,让我们来详细了解一下 Node.js v21.7.1 中的 napi_typedarray_type

首先,N-API 是 Node.js 提供的一个用于构建原生插件的 API。它允许你使用 C 或者 C++ 语言来编写可以直接和 JavaScript 交互的代码片段,这通常在性能要求比较高或者需要使用系统级资源的时候会非常有用。napi_typedarray_type 是 N-API 中的一个功能,用来获取一个 TypedArray 的类型。

TypedArray 是 JavaScript 中的一个特殊的数组对象,它提供了一个底层的二进制缓冲区(binary buffer),可以用来存储多种不同类型的数据,例如整数或浮点数,并且是以字节为单位操作。不同类型的 TypedArray 可以存储不同类型的值,比如 Int32Array 可以存储 32 位整数,Float64Array 可以存储 64 位浮点数等。

现在,当你在原生模块中处理一个从 JavaScript 传递过来的 TypedArray 时,你可能想知道它具体是什么类型的 TypedArray。这就是 napi_typedarray_type 发挥作用的地方。

举个例子,假设你正在编写一个 Node.js 原生模块,需要处理一个从 JavaScript 传递过来的 TypedArray,但你不确定它是 Int32Array 还是其他什么类型的 TypedArray。你可以使用 napi_typedarray_type 来查看:

##include `<`node_api.h>

// ... 在某个原生函数中

// 参数 env 是 napi_env 类型,表示当前的环境上下文
// 参数 value 是 napi_value 类型,表示 JavaScript 传递给原生函数的 TypedArray 对象
void MyFunction(napi_env env, napi_value value) {
    // 用于存储查询结果的变量
    napi_typedarray_type type;
    size_t length;
    void* data;
    size_t offset;

    // 查询 TypedArray 的类型
    napi_status status = napi_get_typedarray_info(env, value, &type, &length, &data, NULL, &offset);

    // 检查 napi_get_typedarray_info 调用是否成功
    if (status == napi_ok) {
        // 根据得到的 type 值对应不同的类型
        switch (type) {
            case napi_int8_array:
                // 处理 Int8Array 类型的 TypedArray
                break;
            case napi_uint8_array:
                // 处理 Uint8Array 类型的 TypedArray
                break;
            // ... 其他 case 分支
        }
    } else {
        // 错误处理
    }
}

在上面的代码中,通过调用 napi_get_typedarray_info 函数并传入 napi_typedarray_type 类型的指针 &type,就可以查询出 TypedArray 的确切类型,并根据类型执行不同的操作。这样你就可以针对不同类型的 TypedArray 编写高效的数据处理逻辑了。

希望这个解释和示例能够帮助你理解 napi_typedarray_type 在 Node.js 中的运用。

Object creation functionsopen in new window

Node.js v21.7.1 中的“Object creation functions”是指 Node.js 提供的一组用于创建和操作 JavaScript 对象的函数,它们都是基于 N-API(Node.js API)实现的。这些函数允许原生模块(通常用 C 或 C++编写)与 JavaScript 代码交互,即你可以在 C/C++代码中创建 JavaScript 对象,并将它们传递给 JavaScript 层面的代码。

N-API 是一个独立于 JavaScript 运行时的 API 层,旨在维护原生插件(native addons)的兼容性 across different versions of Node.js 和不同的 JavaScript engines 比如 V8 (Chrome), Chakra (Microsoft Edge), SpiderMonkey (Firefox), 等等。

下面我们来看几个具体的例子以理解 N-API 提供的对象创建函数的使用:

1. 创建一个新的空对象

在原生模块中,你可能想要创建一个新的空对象并返回给 JavaScript:

napi_value create_empty_object(napi_env env) {
    napi_value result;
    napi_status status = napi_create_object(env, &result);
    if (status != napi_ok) {
        // Handle error...
    }
    return result;
}

在上面的代码中,napi_create_object函数用于创建了一个新的空 JavaScript 对象。env参数是表示当前 N-API 环境的句柄,而result将会被赋值为新创建的对象。

2. 创建一个具有属性的对象

如果你想在对象中添加属性:

napi_value create_object_with_properties(napi_env env) {
    napi_value obj;
    napi_status status = napi_create_object(env, &obj);

    // 设置属性“name”为字符串“Node.js”
    napi_value name_string;
    status = napi_create_string_utf8(env, "Node.js", NAPI_AUTO_LENGTH, &name_string);
    status = napi_set_named_property(env, obj, "name", name_string);

    // 设置属性“version”为数字21.7
    napi_value version_number;
    status = napi_create_double(env, 21.7, &version_number);
    status = napi_set_named_property(env, obj, "version", version_number);

    if (status != napi_ok) {
        // Handle error...
    }
    return obj;
}

在这段代码中,我们首先使用napi_create_object创建了一个空对象,然后使用napi_create_string_utf8创建了一个字符串,接着用napi_set_named_property将这个字符串设置为对象的属性。同样的过程也用来创建一个数值属性。

3. 创建数组

创建 JavaScript 数组的示例:

napi_value create_array_with_numbers(napi_env env) {
    napi_value my_array;
    napi_status status = napi_create_array(env, &my_array);

    // 向数组中填充数字
    for (int i = 0; i `<` 10; i++) {
        napi_value num;
        status = napi_create_int32(env, i, &num);
        status = napi_set_element(env, my_array, i, num);
    }

    if (status != napi_ok) {
        // Handle error...
    }
    return my_array;
}

在这个例子中,napi_create_array用于创建一个新数组,napi_set_element用于设置数组元素。

这些只是简单的例子,但它们展示了如何使用 N-API 的对象创建函数在原生代码中操作 JavaScript 对象。这是构建高效且可跨多个 Node.js 版本运行的原生扩展的关键技术之一。当然,实际开发中需要处理更复杂的逻辑,包括错误处理、异步编程等。

napi_create_arrayopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API 层,它允许你用 C 或 C++ 编写可以直接与 JavaScript 交互的代码。这样做的好处是可以提高性能,或者允许你使用操作系统底层的功能,这在纯 JavaScript 中可能无法实现。

napi_create_array 是 N-API 提供的一个函数,它允许你在原生代码中创建一个新的空数组,并且将这个数组返回给 JavaScript 环境。

这里是 napi_create_array 函数的签名:

napi_status napi_create_array(napi_env env,
                              napi_value* result);
  • env: 这是表示 N-API 环境的句柄,每次 N-API 调用都需要这个环境变量。
  • result: 这是一个指向 napi_value 的指针,它将在函数执行后保存创建的数组对象。

如果调用成功,napi_create_array 会返回 napi_ok 并通过 result 参数返回一个新的数组对象。

下面是如何在 C++ 扩展中使用 napi_create_array 的简单例子:

##include `<`node_api.h>

// 这个函数是一个N-API调用的示例,用来创建一个空数组并返回给JavaScript
napi_value CreateArrayExample(napi_env env, napi_callback_info info) {
  // 定义一个napi_value来存放结果数组
  napi_value array;

  // 创建一个新的空数组
  napi_status status = napi_create_array(env, &array);

  // 检查是否成功创建了数组
  if (status != napi_ok) {
    // 处理错误情况...
  }

  // 返回创建的数组
  return array;
}

// 初始化函数,此函数用来声明和设置模块的导出
napi_value Init(napi_env env, napi_value exports) {
  // 注册函数CreateArrayExample到exports对象,使其在JS中可见
  napi_value create_array_fn;
  napi_create_function(env, NULL, 0, CreateArrayExample, NULL, &create_array_fn);
  napi_set_named_property(env, exports, "createArray", create_array_fn);

  return exports;
}

// N-API模块声明宏
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

假设上面的代码是你的原生模块的一部分,编译并安装之后,你就可以在 Node.js 中这样使用它:

// 导入你的原生模块
const nativeModule = require("your-native-module-name");

// 使用原生模块中的函数来创建一个新的空数组
const newArray = nativeModule.createArray();

console.log(newArray); // 输出: []

在这个例子中,我们定义了一个 CreateArrayExample 函数回调,该函数使用 napi_create_array 创建一个空数组,并将其返回给 JavaScript 端。然后,我们在 Init 函数中将这个回调注册为模块导出的一部分,使得当我们在 JavaScript 中导入这个原生模块时能够调用 createArray 方法来创建数组。

总结一下,napi_create_array 是一个用于从原生模块创建新数组的工具,它允许原生模块与 JavaScript 更紧密地集成和交互。

napi_create_array_with_lengthopen in new window

napi_create_array_with_length 是 Node.js 中用于原生插件开发的一个函数。Node.js 允许你使用 C 或 C++ 语言写扩展模块,这些模块需要使用 N-API(Node API)来和 JavaScript 代码进行交互。napi_create_array_with_length 就是其中一个用于创建数组的 API 函数。

首先,我们需要理解在 Node.js 的环境中,JavaScript 运行在 V8 引擎上。而 N-API 则提供了一层抽象,使得原生模块的代码能够脱离特定版本的 V8 引擎运行,增加了与不同 Node.js 版本之间的兼容性。

现在,让我们详细解释一下 napi_create_array_with_length 的功能:

  • 作用napi_create_array_with_length 函数用来创建一个新的 JavaScript 数组对象,并且可以指定这个数组初始时应该有的长度。

  • 参数

    • napi_env env: 当前的 N-API 环境的句柄(handle),它代表了一个独立的运行时环境,是大多数 N-API 函数调用时必须传入的。
    • size_t length: 你想要创建数组的长度。
    • napi_value* result: 指针变量,用来接收创建好的 JavaScript 数组对象。
  • 返回值: 返回一个 napi_status 值,表示函数调用成功还是失败。如果成功,result 参数将被设置为新创建的数组。

以下是一个简单的使用示例,在一个原生模块中创建一个具有特定长度的数组:

##include `<`node_api.h>

// 假设我们要在一个名为“CreateArray”的函数中使用这个 API
napi_value CreateArray(napi_env env, napi_callback_info info) {
    napi_value array;
    size_t array_length = 10; // 我们想要创建一个长度为10的数组

    // 调用 `napi_create_array_with_length` 来创建数组
    napi_status status = napi_create_array_with_length(env, array_length, &array);

    // 检查是否调用成功
    if (status != napi_ok) {
        // 处理错误...
    }

    // 如果成功,返回新创建的数组
    return array;
}

在实际应用中,你可能会在编写一个需要返回大量数据、或者初始化一个特定长度的数组值时使用到 napi_create_array_with_length。例如,如果你编写的原生模块需要执行一些计算并将结果存储在一个数组中返回给 JavaScript,就可以使用这个 API 来创建这样一个数组。

napi_create_arraybufferopen in new window

好的,让我们深入了解 Node.js 中的 napi_create_arraybuffer 函数。

在 Node.js 中,N-API 是一个用来构建原生插件(native addons)的 API。它让你可以用 C 或 C++ 编写与 JavaScript 代码交云合作执行的代码。这很有用,因为通过原生插件你可以更高效地利用系统资源或执行一些无法直接在 JavaScript 中实现的任务。

napi_create_arraybuffer 是 N-API 的一部分,是一个函数,允许你在原生代码中创建一个 ArrayBuffer 对象。ArrayBuffer 是一个表示固定长度的原始二进制数据缓冲区的类型,在 JavaScript 中通常被用于处理二进制数据。

下面是如何使用 napi_create_arraybuffer 函数的步骤:

  1. 确定要创建的 ArrayBuffer 的大小。 你需要知道你希望分配多少字节的内存。

  2. 调用 napi_create_arraybuffer 函数。 传入要分配的字节数和一个指向指针变量的指针,这个指针变量将会指向 ArrayBuffer 数据的开始处。

  3. 处理函数返回的结果。 napi_create_arraybuffer 调用后将返回一个状态值,表示操作是否成功。

  4. 利用这个 ArrayBuffer。 你可以填充这个 ArrayBuffer,或者把它传回 JavaScript 侧去使用。

现在,让我们通过一个简单的例子来理解这个过程:

假设我们想要创建一个包含 10 个字节的 ArrayBuffer。在 C/C++ 环境下你的代码可能会是这样的:

##include `<`node_api.h>

// 一个示例函数,展示如何创建一个新的 ArrayBuffer
napi_value CreateArrayBufferExample(napi_env env, napi_callback_info info) {
    // 我们想要创建的 ArrayBuffer 的大小
    const size_t byteLength = 10;

    // 这将是指向 ArrayBuffer 数据的指针
    void* data;

    // 这将是最终创建的 ArrayBuffer 对象
    napi_value arrayBuffer;

    // 创建一个大小为 10 个字节的 ArrayBuffer
    napi_status status = napi_create_arraybuffer(env, byteLength, &data, &arrayBuffer);

    // 检查是否成功创建了 ArrayBuffer
    if (status != napi_ok) {
        // 处理错误...
    }

    // 填充 ArrayBuffer,例如:用零初始化它
    memset(data, 0, byteLength);

    // 返回创建的 ArrayBuffer 对象给 JavaScript 使用
    return arrayBuffer;
}

在上面的代码中,我们首先定义了一个函数 CreateArrayBufferExample,它将创建一个新的 ArrayBuffer。我们设定了 ArrayBuffer 的大小是 10 个字节,并且通过 napi_create_arraybuffer 分配了内存。如果函数调用成功,我们会得到一个指向新分配内存的指针 data,以及一个代表 ArrayBuffer 的 N-API value arrayBuffer。然后我们使用 memset 来初始化这段内存(在这个例子里,我们把它全部设置为零)。最后,我们返回 arrayBuffer,它可以在 JavaScript 代码中作为一个 ArrayBuffer 对象使用。

总结起来,napi_create_arraybuffer 是一个非常有用的函数,可以帮助你在原生插件中创建用于处理二进制数据的 ArrayBuffer 对象。通过这种方式,你可以构建性能更优异的应用程序,并且可以在 JavaScript 和原生代码之间有效地传递大型数据块。

napi_create_bufferopen in new window

Node.js 中的napi_create_buffer是一个函数,它属于 N-API(Node API)的一部分。N-API 提供了一种稳定的、版本无关的 API,使得原生模块(即用 C 或 C++编写的扩展模块)可以与 Node.js 的不同版本兼容。

在解释napi_create_buffer之前,我们需要先理解什么是 Buffer。在 Node.js 中,Buffer 是一个用来处理二进制数据的对象。比如说你想从文件中读取数据,或者与网络上的服务器交换数据,通常这些数据都是以二进制形式存在的。Buffer 就是用来表示这些原始的二进制数据的。

现在来看napi_create_buffer这个函数。它的作用是创建一个新的 Buffer 对象,这个 Buffer 对象是在 Node.js 的 C/C++插件(native addon)中使用的。使用这个函数可以让插件创建一个 JavaScript Buffer 实例,然后这个实例可以被返回给 JavaScript 代码使用。

下面是napi_create_buffer的基本用法:

##include `<`node_api.h>

napi_value CreateBuffer(napi_env env, napi_callback_info info) {
  // 定义长度为1024字节的buffer
  size_t length = 1024;
  void* data;
  napi_value buffer;

  // 调用napi_create_buffer来创建一个buffer
  napi_status status = napi_create_buffer(env, length, &data, &buffer);

  if (status != napi_ok) {
    // 处理错误情况
  }

  // 初始化buffer内容,只是示例,实际可能会根据具体需求来操作内存
  memset(data, 0, length);

  // 返回新创建的Buffer对象给JavaScript
  return buffer;
}

// 在插件初始化时注册上面定义的函数
NAPI_MODULE_INIT() {
  napi_value fn;

  napi_create_function(env, NULL, 0, CreateBuffer, NULL, &fn);
  napi_set_named_property(env, exports, "createBuffer", fn);

  return exports;
}

在上面的代码中,我们定义了一个函数CreateBuffer。这个函数被用来创建一个长度为 1024 字节的 Buffer,并且将它初始化为全 0。然后我们把这个 Buffer 对象返回给 JS 代码,JS 代码就可以像使用普通 Buffer 一样使用它。

如何在 Javascript 中使用这个 Buffer 呢?假设上面的 C 代码是我们的插件代码,我们编译并安装之后,在 JS 中可以这样使用:

const myAddon = require("my-addon"); // 加载我们的C/C++插件

const buffer = myAddon.createBuffer(); // 创建一个1024字节的Buffer

console.log(buffer.length); // 输出:1024
console.log(buffer[0]); // 输出:0 因为我们在C代码中将其初始化为0

napi_create_buffer一般在需要在 C/C++插件中处理大量二进制数据时使用。例如,处理图像、音频文件、准备要通过网络发送的数据包等场景。通过这种方式,插件可以创建 Buffer 对象并填充数据,最后将 Buffer 传递回 JavaScript,以便进行进一步的处理或输出。

napi_create_buffer_copyopen in new window

好的,我来解释一下 napi_create_buffer_copy 这个函数在 Node.js 中的作用和如何使用它。

在 Node.js 中,N-API(Node API)是一个用于构建本地插件的接口。本地插件是用 C 或 C++编写的模块,可以直接调用 Node.js 提供的 API,从而实现一些 JavaScript 不能高效处理的任务,比如直接与操作系统进行交互或执行 CPU 密集型计算等。

函数 napi_create_buffer_copy 是 N-API 的一部分,它允许你在原生代码(如 C/C++)中创建一个 Node.js 的 Buffer 对象,并且将一段数据复制到这个新创建的 Buffer 中。

在 JavaScript 中,Buffer 对象通常用于处理二进制数据流,比如网络通信、文件操作等场景。

现在,让我们来看看 napi_create_buffer_copy 的定义:

napi_status napi_create_buffer_copy(napi_env env,
                                    size_t length,
                                    const void* data,
                                    napi_value* result);
  • env:这是一个表示当前 N-API 环境的句柄。
  • length:需要复制的数据长度。
  • data:指向你想要复制到新 Buffer 的数据。
  • result:是一个指针,用于返回新创建的 Buffer 对象。

例子

假设在你的 C/C++扩展中,你想要创建一个包含特定数据的 Buffer 实例,你可以这样使用 napi_create_buffer_copy

##include `<`node_api.h>

napi_value CreateBufferExample(napi_env env, napi_callback_info info) {
    char data[] = "Hello, world!"; // 数据源
    size_t data_length = sizeof(data) - 1; // 计算数据长度,不包括末尾的空字符

    napi_value buffer;
    // 调用 napi_create_buffer_copy 创建 Buffer 并复制数据
    napi_status status = napi_create_buffer_copy(env, data_length, data, &buffer);

    if (status != napi_ok) {
        // 错误处理
        napi_throw_error(env, NULL, "Unable to create buffer");
    }

    return buffer; // 返回新创建的 Buffer 实例
}

在这个例子中,我们定义了一个名为 CreateBufferExample 的函数,它会创建一个包含 "Hello, world!" 文本的 Node.js Buffer 实例。首先,我们声明了一个包含文本信息的 char 数组,然后计算出没有包括末尾空字符的数据长度。通过调用 napi_create_buffer_copy 函数,我们将数据复制到新创建的 Buffer 中,并将其返回给 JavaScript 环境。

在您的 Node.js 扩展模块中导出该函数并从 JavaScript 调用时,您就可以得到一个包含 "Hello, world!" 的 Buffer 对象。例如:

const myAddon = require("./build/Release/myAddon.node");

const buffer = myAddon.CreateBufferExample();
console.log(buffer.toString()); // 输出: Hello, world!

请注意,由于这是一个低级操作,正常情况下你不需要直接使用 N-API 来处理 Buffer,除非你正在编写一个本地扩展模块来增强 Node.js 的能力。在纯 JavaScript 代码中,你可以简单地通过 Buffer.from 方法来创建 Buffer 实例。

napi_create_dateopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。原生插件通常是一些用 C 或 C++编写的扩展,可以直接调用 Node.js 的底层 API,这样你就可以在 Node.js 中执行更高效率的操作,比如直接与硬件交互,或者运行一些 CPU 密集型的计算任务。

napi_create_date 是 N-API 中的一个函数,它允许你在原生模块中创建一个 JavaScript 的 Date 对象。JavaScript 的 Date 对象是用于处理日期和时间的。

首先,我们来看一下napi_create_date的签名(即定义):

napi_status napi_create_date(napi_env env,
                             double time,
                             napi_value* result);

这个函数接受三个参数:

  • env:当前环境的句柄,它代表了 Node.js 运行时的上下文。每当你想要使用 N-API 进行任何操作时,你都需要通过这个环境与 Node.js 的运行时环境交流。
  • time:表示自 1970 年 1 月 1 日 00:00:00 UTC 以来经过的毫秒数,也就是一个时间戳。
  • result:是一个指向 napi_value 的指针,这个指针将被赋值为新创建的 Date 对象。

如果函数执行成功,它会返回napi_ok,这表示没有错误发生。然后,result指针所指向的内存地址会包含一个指向新创建的 Date 对象的引用。

现在,让我们举个例子,假设你正在编写一个原生插件,你想创建一个表示当前时间的 Date 对象:

##include `<`node_api.h>

// 这是你的原生函数实现
napi_value CreateCurrentDate(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value date;

    // 获取当前时间
    double currentTime = (double)time(NULL) * 1000;

    // 创建一个新的Date对象
    status = napi_create_date(env, currentTime, &date);

    // 检查是否有错误发生
    if (status != napi_ok) {
        // 如果有错误,返回undefined
        napi_get_undefined(env, &date);
    }

    // 返回新创建的Date对象
    return date;
}

// 初始化你的插件,注册`CreateCurrentDate`函数
NAPI_MODULE_INIT() {
    napi_value current_date_fn;

    // 把C函数包装为JavaScript可调用的函数
    napi_create_function(env, "createCurrentDate", NAPI_AUTO_LENGTH, CreateCurrentDate, NULL, &current_date_fn);

    // 设置导出的函数
    napi_set_named_property(env, exports, "createCurrentDate", current_date_fn);

    return exports;
}

在这个例子中,我们的CreateCurrentDate函数调用了napi_create_date函数来创建一个新的 Date 对象,并返回它给 JavaScript。JavaScript 代码就能够像下面这样调用你的原生函数,并得到一个 Date 对象:

const nativeAddon = require("你的原生插件名称");
const currentDate = nativeAddon.createCurrentDate();
console.log(currentDate); // 打印当前日期和时间

通过这种方式,你可以在原生插件中创建并操作 JavaScript 对象,这为 Node.js 应用程序提供了更大的灵活性和性能。

napi_create_externalopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API,它允许你在 Node.js 环境中使用 C 或 C++ 代码来创建可以由 JavaScript 代码直接调用的功能。这种方式非常有用,尤其是当你需要执行一些性能敏感或者需要直接与系统资源交互的任务时。

napi_create_external 是 N-API 中的一个函数,它允许你在 JavaScript 中创建一个特殊类型的对象,称为 "外部" 对象。这个外部对象可以用来包装 C/C++ 中的某些数据或资源,以便它们可以在 JavaScript 中被引用和使用,而不必担心如何在 JavaScript 和 C/C++ 数据结构之间进行转换。

下面是 napi_create_external 函数的基本使用方法:

napi_status napi_create_external(napi_env env,
                                 void* data,
                                 napi_finalize finalize_cb,
                                 void* finalize_hint,
                                 napi_value* result);

参数解释:

  • env: 当前的 N-API 环境句柄,代表了当前的 Node.js 上下文。
  • data: 指向你想要在 JavaScript 中表示的 C/C++ 数据的指针。
  • finalize_cb: 一个可选的回调函数,当外部对象被垃圾回收时会被调用,用于清理那些不再需要的资源。
  • finalize_hint: 提供给 finalize_cb 的可选数据,通常用于传递额外的信息。
  • result: 指向 napi_value 变量的指针,创建成功后此变量将包含对应于外部对象的引用。

实际运用例子:

假设你有一个 C 结构体,代表了一个简单的用户信息:

typedef struct {
    char* name;
    int age;
} UserInfo;

现在我们希望在 Node.js 插件中创建一个包装这个 UserInfo 的外部对象,并且在 JavaScript 中使用它。

首先,我们需要一个清理函数,在外部对象被垃圾回收时清理资源:

void finalize_user_info(napi_env env, void* finalize_data, void* finalize_hint) {
    UserInfo* user_info = (UserInfo*)finalize_data;
    // 清理操作,例如释放内存
    free(user_info->name);
    free(user_info);
}

然后,我们可以编写一个函数来创建外部对象并返回给 JavaScript:

napi_value create_user_info(napi_env env, napi_callback_info info) {
    UserInfo* user_info = malloc(sizeof(UserInfo));
    // 假设我们通过某种方式填充了 user_info 结构体

    napi_value user_info_external;
    napi_status status = napi_create_external(env, user_info, finalize_user_info, NULL, &user_info_external);
    if (status != napi_ok) {
        // 处理错误情况
    }

    return user_info_external;
}

最后,在 Node.js 代码中,我们可以像这样使用这个外部对象:

const addon = require("./build/Release/addon");

let userInfo = addon.createUserInfo();
// 此时 userInfo 是一个外部对象,包装了 C 结构体 UserInfo

这样你就可以在 JavaScript 中保存一个指向 C 数据的引用,而且当这个外部对象不再被需要时,N-API 会调用 finalize_user_info 来清理相关资源。这是一个高效管理原生资源的方法,并且允许你在 JavaScript 中利用原生性能。

napi_create_external_arraybufferopen in new window

Node.js 的 N-API (Node.js API) 是一个用来构建原生插件的 API。原生插件是一种可以直接使用系统资源和内存的代码,通常用 C 或 C++编写,并且可以被 Node.js 代码直接调用。napi_create_external_arraybuffer 是 N-API 中的一个函数,它允许你创建一个所谓的“外部” ArrayBuffer。

在 JavaScript 中,ArrayBuffer 是一种代表固定大小的原始二进制数据缓冲区的对象。它们常用于处理像文件、图像或其他二进制流数据这样的底层数据。

napi_create_external_arraybuffer具体来说,它可以让你将一个 C/C++中的已经存在的数据(比如堆上分配的一块内存)作为一个 ArrayBuffer 暴露给 JavaScript 代码,而无需复制数据。这可以提高性能和效率,因为避免了不必要的内存复制。

下面我们将详细解释这个函数和如何使用它:

napi_status napi_create_external_arraybuffer(napi_env env,
                                             void* external_data,
                                             size_t byte_length,
                                             napi_finalize finalize_cb,
                                             void* finalize_hint,
                                             napi_value* result);

参数说明:

  • env: 表示当前的 N-API 环境,它是一个表示 Node.js 运行时上下文的句柄。
  • external_data: 这是指向你要暴露给 JavaScript 的那块已有数据的指针。
  • byte_length: 数据块的大小,以字节为单位。
  • finalize_cb: 当 ArrayBuffer 被垃圾回收时,将会调用这个回调函数。它可以用来释放external_data
  • finalize_hint: 一个传递给finalize_cb的可选提示值,可以用于确定如何清理external_data
  • result: 这个函数执行成功后,将返回新创建的 ArrayBuffer 的 N-API 引用。

实际例子:

假设你有一个 C/C++库,它处理了一些图像操作,并且有一个函数可以生成一个图像数据的数组,你想把这些数据以 ArrayBuffer 的形式暴露给 Node.js:

##include `<`node_api.h>

void FreeImageData(void* data, void* hint) {
  free(data); // 假设这里我们通过malloc分配了图像数据,现在释放它
}

napi_value CreateImageBuffer(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value result;

  // 假设这个函数获取图像数据和大小
  int image_width, image_height;
  unsigned char* image_data = GetImageData(&image_width, &image_height);

  // 图像数据的大小(以字节为单位)
  size_t byte_length = image_width * image_height * sizeof(unsigned char);

  // 创建一个对这块已有数据的引用的ArrayBuffer
  status = napi_create_external_arraybuffer(env, image_data, byte_length, FreeImageData, NULL, &result);
  if (status != napi_ok) {
    napi_throw_error(env, NULL, "Unable to create external ArrayBuffer");
    return NULL;
  }

  return result;
}

// 接下来需要将'CreateImageBuffer'注册到模块当中等更多步骤...

在 Node.js 中,你可能会这么使用它:

const nativeAddon = require("./build/Release/native-addon.node");

// 假设nativeAddon有一个方法叫createImageBuffer,它就是上面定义的CreateImageBuffer
const imageBuffer = nativeAddon.createImageBuffer();

// 现在你拥有了一个ArrayBuffer,其中包含着你的图像数据
// 你可以用TypedArray或者Buffer对象来管理这些数据

这个例子演示了如何使用napi_create_external_arraybuffer将 C/C++中的数据安全且高效地暴露给 Node.js。记住,在实际应用中,需要考虑线程安全和同步问题,特别是如果你打算从多个线程访问数据的话。

napi_create_external_bufferopen in new window

napi_create_external_buffer 是 Node.js 在其 Native API(N-API)中提供的一个函数,用于创建一个"外部"缓冲区(external buffer)。理解这个函数前,我们首先需要知道 Node.js 中的 Buffer 和 N-API 是什么。

在 Node.js 中,Buffer 类是用来处理二进制数据的,它可以用来读写文件、网络通信等操作中传输的数据。而 N-API 则是 Node.js 提供的一个稳定的 API 集,允许你使用 C 或 C++编写原生插件,这些插件可以直接与 Node.js 的 JavaScript 运行时交互。

现在来具体讲解一下 napi_create_external_buffer 函数:

  • 功能:该函数用于创建一个 Buffer 对象,但其内存是由开发者自己管理的,而不是由 Node.js 的垃圾回收机制来管理。这意味着你可以用这个方法将已经存在的数据(比如 C++分配的堆上的数据)包装成一个 Node.js 可用的 Buffer 对象,而无需复制数据。

  • 参数

    1. env: 当前的 napi 环境,代表了 Node.js 环境的一个句柄。
    2. length: 要创建的 Buffer 的长度。
    3. data: 指向预先分配好的数据的指针。
    4. finalize_cb: 一个可选的回调函数,当这个 Buffer 被垃圾回收时会被调用,这个函数通常用来释放外部分配的内存。
    5. finalize_hint: 传递给finalize_cb的可选参数。
    6. result: 这是一个输出参数,创建成功后返回的 Buffer 对象。
  • 返回值:如果成功,返回napi_ok,否则返回一个错误码。

下面通过一个例子来说明 napi_create_external_buffer 的使用:

想象一下,你在 C++中有一块内存,里面存放着音频数据,现在你希望在一个 Node.js 应用中处理这些数据,但你不希望进行数据的复制以避免额外的性能开销。

##include `<`node_api.h>

// 假设这是你的音频数据和清理函数
const size_t AUDIO_DATA_SIZE = 1024;
char* audioData = new char[AUDIO_DATA_SIZE];

void FreeAudioData(napi_env env, void* finalize_data, void* finalize_hint) {
  char* data = reinterpret_cast`<`char*>(finalize_data);
  delete[] data;
}

// 这个函数将被暴露给Node.js
napi_value CreateExternalBuffer(napi_env env, napi_callback_info info) {
  napi_value externalBuffer;
  // 创建一个外部缓冲区,将音频数据包装成Node.js的Buffer对象
  napi_status status = napi_create_external_buffer(env,
                                                   AUDIO_DATA_SIZE,
                                                   audioData,
                                                   FreeAudioData,
                                                   nullptr,
                                                   &externalBuffer);

  if (status != napi_ok) {
    // 处理错误
  }

  return externalBuffer;
}

// 初始化代码和模块注册...

在上面的例子中,我们创建了一个外部缓冲区来引用我们已经有的音频数据,并且定义了一个清理函数FreeAudioData,当 Node.js 判断这个 Buffer 不再被需要时,它会自动调用这个清理函数来释放内存。这样我们就可以有效地在 Node.js 中处理我们的音频数据而不需要拷贝它。

最后在 Node.js 中,你可以像这样使用这个新建的 Buffer:

const nativeAddon = require("./build/Release/nativeAddon.node");

// 获取由C++创建的外部Buffer
const audioBuffer = nativeAddon.CreateExternalBuffer();

// 做一些处理,比如播放或者修改音频数据

通过这种方式,我们实现了 C++和 Node.js 之间高效的二进制数据交换。这对于性能敏感型的应用非常重要,比如处理多媒体内容、大量科学计算数据等场景。

napi_create_objectopen in new window

好的,既然你对编程还比较新,我会尽量用简单的语言来解释。首先,napi_create_object 是 Node.js 中 N-API 的一部分,N-API 是一个 C 语言的接口,允许你创建和操作 JavaScript 对象从 C 或者 C++代码中。

在 Node.js 中,许多时候我们想要使用 C 或 C++扩展性能或者访问一些底层系统资源,这就是 N-API 发挥作用的地方。通过 N-API,你可以构建原生的插件,而napi_create_object 就是用来创建一个空的 JavaScript 对象的函数。

现在我会通过一个实际的例子来说明napi_create_object 是如何工作的。假设我们正在编写一个原生模块,我们需要从 C 代码中创建一个新的 JavaScript 对象返回给 JavaScript 环境。

##include `<`node_api.h>

// 假设这个函数是暴露给JavaScript的功能
napi_value CreateObject(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value myObject;

    // 使用 napi_create_object 创建一个新的 JavaScript 对象
    status = napi_create_object(env, &myObject);

    if (status != napi_ok) {
        // 如果创建对象失败,抛出一个错误
        napi_throw_error(env, NULL, "Unable to create object");
        return NULL;
    }

    // 假设我们在这个对象上设置了一些属性...
    // napi_set_named_property(env, myObject, "key", someValue);

    // 返回这个新创建的对象
    return myObject;
}

// 初始化函数,将CreateObject绑定到exports上,让JS可以调用它
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    // 创建一个函数值
    status = napi_create_function(env, NULL, 0, CreateObject, NULL, &fn);
    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Unable to wrap native function");
    }

    // 将这个函数作为一个属性添加到exports对象上
    status = napi_set_named_property(env, exports, "createObject", fn);
    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Unable to populate exports");
    }

    return exports;
}

// 定义模块,其中 "addon" 是模块名
NAPI_MODULE(addon, Init)

在这个示例中:

  1. CreateObject 函数是我们想要在 JavaScript 环境中调用的 C 函数。
  2. 我们使用napi_create_object 来创建一个新的空白 JavaScript 对象。
  3. 然后我们可以使用其它 N-API 函数像napi_set_named_property 给这个对象设置属性或者方法。
  4. 最后,CreateObject 返回这个新创建的对象到 JavaScript 环境。

这样,当你在 Node.js 环境中安装并引入这个原生模块时,你可以这样使用它:

const addon = require("./build/Release/addon");

let obj = addon.createObject();
console.log(obj); // 输出: {}

在 JavaScript 代码中,addon.createObject() 调用了我们用 C 语言编写的函数,并且返回了一个新的 JavaScript 对象。

理解napi_create_object和其它 N-API 函数的关键点在于:它们提供了 JavaScript 和 C/C++之间的桥梁,允许开发者利用 C/C++的性能和系统级访问权限来扩展 Node.js 的功能。

napi_create_symbolopen in new window

napi_create_symbol 是 Node.js 中 N-API 的一个函数,它允许原生模块创建 JavaScript 的 Symbol 类型的值。在 ES6 (ECMAScript 2015) 中引入的 Symbol 是一个基本数据类型,用于创建唯一的标识符。Symbols 非常适合用作对象属性的键,因为每个 Symbol 都是唯一的,不会与其他属性的键发生冲突。

首先,我们来了解下 JavaScript 中的 Symbol 是什么:

// 在 JavaScript 中创建 Symbol
const mySymbol = Symbol("my unique identifier");
console.log(mySymbol); // 输出:Symbol(my unique identifier)

上面的代码展示了在 JavaScript 中如何创建一个 Symbol。Symbols 通常用于创建独一无二的标识符。

现在,如果你正在编写一个 Node.js 的原生扩展模块(可能使用 C 或者 C++),你可能会需要在这些低级语言中创建 JavaScript 对象和类型。N-API 是 Node.js 提供的一个 C API,让你能够构建这类原生插件,并且保证与 Node.js 版本之间的兼容性。

使用 napi_create_symbol 函数可以在原生代码中创建一个新的 JavaScript Symbol。下面是一个实际的例子,展示如何在一个原生模块中使用 napi_create_symbol

假设你有以下的 C 代码:

##include `<`node_api.h>

// 这个函数被调用以创建一个新的 Symbol
napi_value CreateSymbol(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value symbolDescription;
    napi_value result;

    // 首先,我们创建一个带有描述的 JS 字符串
    status = napi_create_string_utf8(env, "my unique identifier", NAPI_AUTO_LENGTH, &symbolDescription);
    if (status != napi_ok) return NULL;

    // 接下来,使用该描述创建一个 Symbol
    status = napi_create_symbol(env, symbolDescription, &result);
    if (status != napi_ok) return NULL;

    // 返回这个新创建的 Symbol
    return result;
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    // 将上面的 CreateSymbol 函数暴露给 JavaScript
    status = napi_create_function(env, NULL, 0, CreateSymbol, NULL, &fn);
    if (status == napi_ok) {
        napi_set_named_property(env, exports, "createSymbol", fn);
    }

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

这段代码演示了如何在原生模块中创建一个函数 CreateSymbol ,这个函数当从 JavaScript 调用时会返回一个新的 Symbol。在模块初始化函数 Init 中,我们将 CreateSymbol 函数导出为模块的一个属性,使得 JavaScript 代码可以调用它。

在 JavaScript 中使用这个原生模块可能像这样:

const nativeAddon = require("./build/Release/native-addon.node");

// 从原生模块中创建一个 Symbol
const sym = nativeAddon.createSymbol();

console.log(sym); // 输出:Symbol(my unique identifier)

通过这个例子,你可以看到 napi_create_symbol 如何在原生模块中被用来创建 JavaScript 中的 Symbol 类型,同时使得这个功能可以被 JavaScript 代码所调用。

node_api_symbol_foropen in new window

Node.js 中的 napi_symbol_for 是一个函数,它属于 Node.js 的 N-API(原生 API),这是一套用于构建原生插件的 API。在详细解释这个函数之前,我们得先理解几个概念。

关键概念:

  1. Symbol: 在 JavaScript 中,Symbol 是一种原始数据类型,像 Number、String 或 Boolean 一样。每个 Symbol 值都是唯一不变的,通常用作对象属性的键,以保证不会与其他属性键发生冲突。

  2. N-API:N-API 是 Node.js 提供的一套 C 语言 API,它允许你编写能够与 Node.js 交互的本地插件。通过 N-API,你可以创建一些性能更高、更接近操作系统底层的代码。

  3. node_api_symbol_for:这个函数是 N-API 提供的,用来创建或获取全局符号注册表中的 Symbol,这意味着当你在不同的模块或者插件中使用相同名称创建 Symbol 时,通过 napi_symbol_for 总是能够拿到同一个 Symbol 实例。

node_api_symbol_for 函数的作用和运用:

node_api_symbol_for 函数的目的是让不同的原生插件共享相同的 Symbol 值,而不是每个插件独立创建自己的 Symbol。这有助于避免意外的命名冲突,因为 Symbol 值即使名称相同,如果独立创建,也会是不同的值。

实际应用例子:

假设你正在开发两个不同的 Node.js 原生插件,它们都需要引用一个名为 "shared_event" 的事件名称。为了确保这两个插件引用的是同一个事件名称,你可以在每个插件中使用 napi_symbol_for 来获取这个事件的 Symbol 值。

// 插件A中的代码
napi_value symbol_for_plugin_a;
napi_status status = napi_symbol_for(env, "shared_event", &symbol_for_plugin_a);
if (status != napi_ok) {
    // 处理错误
}

// 插件B中的代码
napi_value symbol_for_plugin_b;
napi_status status = napi_symbol_for(env, "shared_event", &symbol_for_plugin_b);
if (status != napi_ok) {
    // 处理错误
}

// 以上两段代码即使在不同的插件中,通过 napi_symbol_for 获取到的 symbol_for_plugin_a 和 symbol_for_plugin_b 是相同的。

这样,无论哪个插件发布或监听 "shared_event" 事件,它们都会指向同一个事件标识符,确保了通信的一致性。

使用场景总结:

  • 当多个原生插件需要对某个特定的功能或事件进行协作时。
  • 当你想要避免全局空间中的命名冲突。
  • 当在原生插件中实现跨模块的符号共享时。

希望以上解释和例子能帮助你理解 node_api_symbol_for 在 Node.js 中的作用和如何使用它。

napi_create_typedarrayopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API 层,它提供了与 V8 和其他 Node.js 内部组件相互操作的接口,并且是跨 Node 版本稳定的。napi_create_typedarray 函数是 N-API 的一部分,它允许你在原生代码中创建一个 TypedArray 对象,这个对象可以被 JavaScript 代码使用。

TypedArray 是 JavaScript 的一种数据类型,用于表示一个固定长度的二进制数据缓冲区。对于需要处理二进制数据(如文件系统操作、网络通信、或者图像处理等)的应用来说非常有用。

下面将详细解释 napi_create_typedarray 函数和一个简单实例。

函数的定义:

napi_status napi_create_typedarray(
    napi_env env,                  // [in] N-API 环境句柄
    napi_typedarray_type type,     // [in] 要创建的 TypedArray 类型
    size_t length,                 // [in] 数组中元素的数量
    napi_value arraybuffer,        // [in] ArrayBuffer 对象
    size_t byte_offset,            // [in] 在 ArrayBuffer 中开始的字节偏移量
    napi_value* result             // [out] 新创建的 TypedArray 引用
);

参数说明:

  1. env: 这是一个代表当前 N-API 环境的句柄,每次调用 N-API 函数时都需要传递。
  2. type: 指定你想创建的 TypedArray 的具体类型,比如 napi_uint8_array, napi_int16_array 等。
  3. length: 数组的长度,即你希望创建的数组中会有多少个元素。
  4. arraybuffer: 一个已存在的 ArrayBuffer 对象,TypedArray 将会使用这个 ArrayBuffer 作为数据源。
  5. byte_offset: 在给定的 ArrayBuffer 中的起始位置,也就是从哪里开始创建 TypedArray。
  6. result: 这是一个输出参数,创建成功后,新的 TypedArray 的引用将会被存储在这里。

返回值:返回一个 napi_status 枚举值,表示函数调用是否成功。

示例:

假设我们想在原生模块中创建一个长度为 10 的 Uint8Array(无符号 8 位整数数组),并填充一些数据然后返回给 JavaScript 使用。

##include `<`node_api.h>

// 假设这个函数是被触发的原生函数
napi_value CreateUint8Array(napi_env env, napi_callback_info info) {
    // 定义一个指向结果 TypedArray 的变量
    napi_value result_array;

    // 创建一个 ArrayBuffer。假设我们已经创建了一个 buffer 变量。
    napi_value buffer;

    // 我们假设 buffer 已经被正确创建,并且有足够的空间 (至少 10 bytes)

    // 创建 TypedArray
    napi_status status = napi_create_typedarray(env, napi_uint8_array, 10, buffer, 0, &result_array);

    // 检测是否创建成功
    if (status != napi_ok) {
        // 处理错误...
    }

    // 返回新创建的 TypedArray 给 JavaScript
    return result_array;
}

// 注册原生函数等...

在上面的例子中,我们首先声明了一个 napi_value 类型的 result_array 来存放我们创建好的 TypedArray。之后,我们调用 napi_create_typedarray,指定了我们要创建的是 napi_uint8_array 类型的数组,长度为 10,基于已有的 buffer ArrayBuffer,从第 0 个字节开始创建。如果一切顺利,我们将得到一个指向新 Uint8Array 的引用,并将其返回给 JavaScript 代码使用。

在 JavaScript 层面,你可以像使用常规的 Uint8Array 那样使用这个原生模块返回的对象,比如遍历元素或者将它传递给其他需要二进制数据的 API。

napi_create_dataviewopen in new window

好的,我会尽量通俗易懂地解释给你。

napi_create_dataview() 是 Node.js 中 N-API(Native API)的一个函数,这个 API 允许原生插件作者用 C 或 C++写代码,然后可以在 Node.js 中调用这些代码。N-API 的目的是提供一个与 Node.js 版本无关的 API,这样编写的原生代码就不需要针对不同版本的 Node.js 重新编译。

现在,来看napi_create_dataview()这个函数。DataView 是 JavaScript 的一种对象,用于读取和写入二进制数据缓冲区(ArrayBuffer)。在 JavaScript 中,ArrayBuffer 是一种固定长度的原始二进制数据缓冲区,而 DataView 则提供了一个更灵活的接口来操作这些二进制数据。

napi_create_dataview() 函数就是用来在原生模块代码中创建一个与 JavaScript 层面上的 DataView 对象相对应的 N-API 版本的 DataView。通过这个函数,你可以指定你想要操作的 ArrayBuffer 的哪一部分,以及开始操作的字节偏移量和长度。

下面是一个简单的例子:

##include `<`node_api.h>

// 假设我们有一个原生函数,想要创建一个DataView
napi_value CreateDataView(napi_env env, napi_callback_info info) {
    size_t byte_offset = 0; // 我们从缓冲区的起始位置开始
    size_t length = 8; // 假设我们想操作8个字节

    // 我们需要一个已经存在的ArrayBuffer
    napi_value arraybuffer;
    // ... 这里省略了创建或者获取ArrayBuffer的过程

    // 创建DataView
    napi_value dataview;
    napi_status status = napi_create_dataview(env, length, arraybuffer, byte_offset, &dataview);
    if (status != napi_ok) {
        // 错误处理...
    }

    // 返回创建的DataView对象
    return dataview;
}

在这个例子中,我们定义了一个CreateDataView函数,它会创建一个字节长度为 8 的 DataView,并且从给定的 ArrayBuffer 的起始位置开始。返回的dataview对象可以在随后的原生代码中使用,或者传递回 JavaScript 进行进一步的操作。

实际上,您可能会使用napi_create_dataview()来构建能够处理图像数据、音频流、网络包或任何其他需要精确字节级别操作的二进制数据的库。总之,这是一个连接 JavaScript 高级功能与底层二进制数据操作之间的桥梁。

Functions to convert from C types to Node-APIopen in new window

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎的 JavaScript 运行环境。Node.js 允许你在服务器端运行 JavaScript,而 Node-API(之前称为 N-API)是一个用来构建原生插件的 API。原生插件是用 C 或 C++编写的模块,它们可以被 Node.js 直接调用。

通常情况下,当你使用 JavaScript 开发 Node.js 应用时,你不需要关心 C/C++代码或者 Node-API。但如果你想要提高性能,访问系统底层特性,或者复用现有的 C/C++库,你可能会需要编写原生插件。

Node-API 中的 "Functions to convert from C types to Node-API" 部分提供了一系列函数,这些函数允许你将 C 语言中的数据类型转换为 Node-API 能够识别的类型。这样,你就可以在 Node.js 中使用 C/C++代码定义的数据了。

例如:

  1. 假设你有一个 C 函数,它计算两个整数的和,并且返回结果。为了让这个 C 函数能够和 Node.js 交互,你需要将 C 中的整型(int)转换为 Node-API 中的数值类型(napi_value):
##include `<`node_api.h>

// 原始的C函数
int add(int a, int b) {
    return a + b;
}

// 为Node.js封装的函数
napi_value AddWrapped(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 2;
    napi_value args[2];

    // 获取JavaScript传入的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 转换JavaScript参数到C类型
    int value1, value2;
    status = napi_get_value_int32(env, args[0], &value1);
    status = napi_get_value_int32(env, args[1], &value2);

    // 调用原始的C函数
    int result = add(value1, value2);

    // 将C的结果转换回Node-API类型
    napi_value sum;
    status = napi_create_int32(env, result, &sum);

    return sum;
}

在这个例子中,napi_get_value_int32 函数用于从 JavaScript 传递的参数中获取 C 的 int 类型值,而 napi_create_int32 则用来创建一个新的 Node-API 类型的整数。

  1. 如果你有一个 C 结构体,表示一个二维的点,你也可以将这个结构体通过 Node-API 暴露给 JavaScript:
##include `<`node_api.h>

typedef struct {
    double x;
    double y;
} Point;

// 相应的转换函数可能会这样写:
napi_value PointToNapiObject(napi_env env, Point* point) {
    napi_status status;
    napi_value obj;
    napi_value x, y;

    // 创建一个空对象
    status = napi_create_object(env, &obj);

    // 将Point结构体的x和y属性转换为napi_value
    status = napi_create_double(env, point->x, &x);
    status = napi_create_double(env, point->y, &y);

    // 设置JavaScript对象的属性
    status = napi_set_named_property(env, obj, "x", x);
    status = napi_set_named_property(env, obj, "y", y);

    return obj;
}

注意:在实际编码过程中,你应该总是检查 napi_status 返回值以确保每个 API 调用都成功了。

上述示例展示了如何将 C 语言数据类型转换为 JavaScript 可以理解的数据类型。通过这种方式,Node.js 可以与原生模块互操作,同时 Node-API 提供的抽象层可以使得模块编写更为简单,同时还具备跨 Node.js 版本的兼容性。

napi_create_int32open in new window

当然,我乐意帮助你。首先,我们需要了解几个关键点:

  1. Node.js:这是一个使用 JavaScript 语言来编写服务器端程序的平台。
  2. N-API:这是 Node.js 提供的一个底层 API,用于构建本地插件。本地插件是用 C 或 C++等语言编写的模块,可以直接调用 Node.js 的各种功能。

napi_create_int32 是 N-API 中的一个函数,它用于创建一个包含 32 位整数值的N-API值。在 JavaScript 中,当我们处理数字时,通常不需要考虑数字的具体类型,但在 C 或 C++等低级语言中,我们需要明确指定使用哪一种类型的数字,因为每种类型的数字在内存中占用的空间和表达的范围都有所不同。int32是指一个 32 位的整数。

现在,让我们给出一个实际的场景,比如说你想要编写一个 Node.js 本地插件来执行一些性能敏感的数学运算。此时,你可能会选择使用 C++来实现这些运算,以便利用它的性能优势。

在 C++代码中,你希望建立一个与 JavaScript 代码交互的桥梁,以便将计算结果传递回 JavaScript 环境。napi_create_int32就是在这里发挥作用的函数之一。以下是一个简化的例子:

##include `<`node_api.h>

// 假设这个函数将被暴露给JavaScript,并且它返回一个计算结果
napi_value CalculateSomething(napi_env env, napi_callback_info info) {
    napi_value result;

    int32_t value = 123; // 假设这是通过某种计算得到的32位整数结果

    // 使用napi_create_int32来创建一个N-API表示的32位整数
    napi_status status = napi_create_int32(env, value, &result);

    if (status != napi_ok) {
        // 如果创建失败,抛出一个错误
        napi_throw_error(env, NULL, "Failed to create int32 value");
        return nullptr;
    }

    // 返回创建好的整数值给JavaScript
    return result;
}

// 其他必要的注册代码...

在上面的代码中,我们定义了一个CalculateSomething函数,它通过napi_create_int32创建了一个 32 位整数的 N-API 值,并返回给 JavaScript。如果操作成功,JavaScript 代码就可以接收到这个整数值并利用它做进一步的处理。如果创建失败,则会抛出一个错误。

在 JavaScript 端,调用这个本地模块可能看起来像这样:

const nativeAddon = require("./build/Release/native-addon");

const result = nativeAddon.CalculateSomething();
console.log(result); // 输出: 123

在这个例子中,nativeAddon.CalculateSomething() 调用了我们刚才用 C++编写并暴露给 JavaScript 的函数,并打印出了从 C++返回的整数123

希望这个解释和例子能帮助你更好地理解napi_create_int32函数在 Node.js 的 N-API 中的运用。

napi_create_uint32open in new window

Node.js 的 N-API 是一个用于构建原生插件的 API,它提供了一组用 C 或 C++ 编写与 JavaScript 交互的函数。在 Node.js 中,原生插件是一种使用其他语言编写的模块,能够直接运行在操作系统级别,通常用于执行那些对性能要求较高的任务。

napi_create_uint32 是 N-API 中的一个函数,其作用是创建一个包含无符号 32 位整数(uint32)的 JavaScript 数值。

以下是 napi_create_uint32 函数的定义:

napi_status napi_create_uint32(napi_env env,
                                uint32_t value,
                                napi_value* result);

参数说明:

  • env:表示当前的 N-API 环境上下文,这通常在函数调用时由 Node.js 自动传递。
  • value:这是你想要创建的 JavaScript 数值中存储的实际的无符号 32 位整数值。
  • result:这是一个指向 napi_value 的指针,用于接收创建出来的 JavaScript 数值。

返回值: 这个函数会返回一个类型为 napi_status 的状态码,表示操作成功或失败的状态。如果函数执行成功,状态码将会是 napi_ok

让我们举一个例子来说明它的作用:

假设你正在编写一个原生插件,需要从 C/C++ 层面计算某种资源的数量,并将这个数量传递给 JavaScript。

在 C/C++ 插件代码中,你可能会有如下的函数:

##include `<`node_api.h>

// ... 其他必要的头文件和代码 ...

napi_value GetResourceCount(napi_env env, napi_callback_info info) {
    // 假设我们通过某种方式计算得到了资源数量
    uint32_t resourceCount = CalculateResourceCount();

    // 创建一个 JavaScript 数值来保存这个无符号32位整数
    napi_value jsResourceCount;
    napi_status status = napi_create_uint32(env, resourceCount, &jsResourceCount);

    // 检查是否成功创建 JavaScript 数值
    if (status != napi_ok) {
        // 如果创建失败,则抛出错误
        napi_throw_error(env, NULL, "Unable to create uint32 number");
    }

    // 返回这个 JavaScript 数值
    return jsResourceCount;
}

// 注册函数到 Node.js
NAPI_MODULE_INIT() {
    napi_value exportFn;
    napi_create_function(env, NULL, 0, GetResourceCount, NULL, &exportFn);
    napi_set_named_property(env, exports, "getResourceCount", exportFn);
    return exports;
}

在 JavaScript 代码中,你可以加载并使用这个插件:

const nativeAddon = require("./build/Release/native-addon.node");

// 获取资源数量
const resourceCount = nativeAddon.getResourceCount();
console.log("资源数量:", resourceCount);

上述例子展示了如何在原生插件中使用 napi_create_uint32 来创建一个无符号 32 位整数并将其以 JavaScript 数值的形式传递给 JavaScript 代码。这使得原生代码与 JavaScript 代码之间的数据交换变得简单、安全。

napi_create_int64open in new window

Node.js 是一个基于 Chrome V8 引擎执行 JavaScript 代码的平台。而 N-API 则是 Node.js 提供的一套 C API,允许原生插件的作者编写不依赖于 JavaScript 运行时版本的代码,这样可以大大提高原生模块的稳定性和兼容性。

napi_create_int64 是 N-API 中的一个函数,它用于在原生代码中创建一个表示 64 位整数的 JavaScript 数值。在 JavaScript 中虽然所有数字都是以双精度浮点数的形式存储的,但在与操作系统或者其他低级系统交互时,我们经常需要处理整数值,尤其是范围较大的整数(比如文件大小、内存地址等),这时候就需要使用像 napi_create_int64 这样的 API 来确保数字的准确性。

下面我会给你展示一个使用 napi_create_int64 的简单例子:

假设我们想在 Node.js 的原生扩展中创建一个函数,它会接收一个 64 位整数作为参数,并返回它的值。首先,我们需要编写 C 代码来实现这个原生函数:

##include `<`node_api.h>

// 定义一个原生的 N-API 函数
napi_value GetInt64Value(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value the_number;

    // 假设我们要返回的 64 位整数是 123456789012345
    int64_t my_int64 = 123456789012345;

    // 使用 napi_create_int64 创建一个 JavaScript 数值
    status = napi_create_int64(env, my_int64, &the_number);
    if (status != napi_ok) {
        // 处理错误情况
        napi_throw_error(env, NULL, "Failed to create 64-bit integer");
        return NULL;
    }

    // 返回创建的整数值
    return the_number;
}

// 初始化函数,注册上面定义的 'GetInt64Value' 函数
NAPI_MODULE_INIT() {
    napi_value fn;

    // 创建一个 JavaScript 函数
    napi_create_function(env, NULL, 0, GetInt64Value, NULL, &fn);

    // 给这个函数命名为 "getInt64Value" 并导出
    napi_set_named_property(env, exports, "getInt64Value", fn);
    return exports;
}

接下来,我们需要编译这个原生扩展使之成为 Node.js 可以加载的模块。编译后,我们可以在 JavaScript 代码中这样使用它:

// 加载原生模块
const nativeAddon = require("./build/Release/native-addon.node");

// 调用我们刚才实现的原生方法
const int64Value = nativeAddon.getInt64Value();

console.log(int64Value); // 输出:123456789012345

在这个简单的例子中,我们编写了一个 C 函数,通过 napi_create_int64 创建了一个 JavaScript 能够识别的 64 位整数,并将其作为函数的返回值。然后在 JavaScript 代码中,我们加载并调用了这个原生模块函数,最终打印出这个整数的值。

需要注意的是,这个过程涉及到对 Node.js 原生模块开发的理解,包括 C/C++ 编程知识、构建工具(如 node-gyp)的使用,以及 Node.js 的 N-API。如果你仅仅是 JavaScript 开发者,通常不需要深入了解这一层,除非你需要编写或维护 Node.js 的原生扩展。

napi_create_doubleopen in new window

在 Node.js 中,N-API 是一个用来构建原生插件的 API。原生插件是用 C 或 C++编写的模块,它们可以直接调用 Node.js 提供的 API,实现一些 JavaScript 难以做到的性能优化或直接与操作系统底层交互的功能。

napi_create_double是 N-API 中的一个函数,它的作用是创建一个表示双精度浮点数(double)的 JavaScript 数值。当你在原生插件中使用 C 或 C++处理双精度浮点数时,如果你想将这个数值传递回 JavaScript,就需要把它转换成 JavaScript 能够识别和处理的数值类型。

下面是一个简单的例子说明如何使用napi_create_double函数:

##include `<`node_api.h>

// 这个函数是C/C++扩展暴露给JavaScript的一个函数.
// 假设我们要计算一个C语言中的双精度浮点数,并将结果返回给JavaScript.
napi_value CalculatePi(napi_env env, napi_callback_info args) {
  double pi = 3.14159265359; // 在C语言中定义一个双精度浮点数pi

  // 声明一个napi_value变量,这将是我们创建的JavaScript数值的句柄
  napi_value js_pi;

  // 使用napi_create_double将C语言中的pi转换成JavaScript数值
  napi_status status = napi_create_double(env, pi, &js_pi);

  // 检查是否成功创建了数值
  if (status != napi_ok) {
    // 如果创建失败,返回错误
    napi_throw_error(env, NULL, "Unable to create a double value");
    return NULL;
  }

  // 如果成功,返回创建的JavaScript数值
  return js_pi;
}

// 初始化函数,注册CalculatePi函数
NAPI_MODULE_INIT() {
  napi_value export_fn;

  // 创建一个JavaScript函数,当JavaScript代码调用它时,会执行CalculatePi
  napi_create_function(env, "calculatePi", NAPI_AUTO_LENGTH, CalculatePi, NULL, &export_fn);

  // 将这个函数作为模块的导出
  napi_set_named_property(env, exports, "calculatePi", export_fn);

  return exports;
}

上述代码中,我们定义了一个名为CalculatePi的 C 函数,该函数会计算圆周率 π 的一个近似值,并通过使用napi_create_double函数将这个双精度浮点数值封装为 JavaScript 可以理解的数值类型。之后,我们可以在 JavaScript 代码中调用这个函数并得到 π 的值。

例如,在 Node.js 中,你可以像这样使用这个扩展:

const myAddon = require("./build/Release/my_addon"); // 替换成你的扩展名

console.log(myAddon.calculatePi()); // 输出:3.14159265359

在这个 JavaScript 代码示例中,我们加载了我们刚才编写的原生模块,并调用calculatePi方法,它会输出我们从 C 扩展中获取的 π 值。

napi_create_bigint_int64open in new window

Node.js 中的napi_create_bigint_int64是一个 N-API 函数,它允许你在原生插件代码中创建一个新的 BigInt 类型的变量。这个函数特别适用于需要处理大整数(超出 JavaScript Number 能精确表示的范围)的情况。

N-API 是 Node.js 提供的一个稳定的 API 层,它允许开发者用 C 或 C++编写扩展模块,与 Node.js 进行交互。使用 N-API 编写的模块不依赖于特定版本的 V8 引擎,这意味着模块更容易保持跨 Node.js 版本的兼容性。

下面我们详细解释一下napi_create_bigint_int64

函数签名

napi_status napi_create_bigint_int64(napi_env env,
                                      int64_t value,
                                      napi_value* result);

这个函数有三个参数:

  1. napi_env env: 当前的环境句柄,它代表了当前的执行上下文。
  2. int64_t value: 你想要转换成 BigInt 的 64 位整数值。
  3. napi_value* result: 这是一个指针,函数会把创建的 BigInt 赋值到这里。

函数返回一个napi_status类型的值,这代表操作的结果。如果一切顺利,返回napi_ok

实际例子

假设你正在开发一个 Node.js 原生模块,这个模块需要处理大量的数据,例如加密或者文件系统操作。而这些操作涉及到的数字可能非常大,大到不能用标准的 JavaScript 数字类型(Number)来精确地表示。

例如,我们需要创建一个表示文件大小的 BigInt,文件大小为9223372036854775807字节(这个值接近 64 位整型的最大值),以下是在 C++中使用napi_create_bigint_int64的示例代码:

##include `<`node_api.h>

// 假设这个函数作为一个原生模块导出给JS调用
napi_value GetFileSizeAsBigInt(napi_env env, napi_callback_info info) {
  // 存储最终BigInt结果的变量
  napi_value result;

  // 文件大小
  int64_t fileSize = 9223372036854775807;

  // 创建BigInt
  napi_status status = napi_create_bigint_int64(env, fileSize, &result);

  // 检查是否成功创建BigInt
  if (status != napi_ok) {
    // 创建失败,可能需要处理错误
    // 例如抛出异常等
  }

  // 返回创建好的BigInt给JavaScript
  return result;
}

// 其他将函数导出到JS的代码...

当 JavaScript 代码调用这个原生模块时,它将会得到一个 BigInt 对象,可以安全地表示和操作这个巨大的数字。

总结一下,napi_create_bigint_int64在 Node.js 的原生模块开发中用于创建能表示超大整数的 BigInt 对象,从而使得这些模块能够处理超出 JavaScript Number 类型能力范围的数据。

napi_create_bigint_uint64open in new window

当然,让我来详细解释一下 Node.js 中的 napi_create_bigint_uint64 这个函数。

在 Node.js 中,N-API(Node API)是一个用于构建本地插件(native addons)的 API 层,它允许 C 和 C++代码与 JavaScript 交互。这意味着你可以用 C 或 C++编写一些性能密集或者系统级别的操作,并通过 N-API 暴露给你的 Node.js 应用程序使用。

napi_create_bigint_uint64 是 N-API 的一部分,它用于创建一个表示大整数(BigInt)的 JavaScript 值。由于 JavaScript 中的 Number 类型不能安全地表示所有的 64 位整数(因为其双精度浮点格式只能精确表示到 53 位),BigInt 类型就出现了,它可以表示任意大小的整数。

函数原型:

napi_status napi_create_bigint_uint64(
    napi_env env,
    uint64_t value,
    napi_value* result);
  • env: 这是 N-API 调用的环境上下文,用于代表当前的 Node.js 环境。
  • value: 这是你想要转换为 BigInt 的无符号 64 位整数(uint64_t)。
  • result: 这是一个指向 napi_value 的指针,该函数会将创建的 BigInt 值存储在这里返回。

使用场景:

假设你正在编写一个 Node.js 本地插件,需要处理非常大的整数,比如文件系统中大文件的字节大小,或者某些高精度时间戳等。在 C/C++中,你可以使用标准的 64 位整数来精确表示这些值,但是如果你需要将这些值传递给 JavaScript,就会遇到精度问题。使用 napi_create_bigint_uint64 函数,你可以将 C/C++中的 64 位整数安全地转换为 JavaScript 的 BigInt。

实际例子:

想象一下,你有一个 C 函数,它返回了一个很大的文件的大小(以字节为单位),这个数字可能会超过 JavaScript Number 类型的安全整数范围。你可以使用 napi_create_bigint_uint64 将这个值转换为 BigInt:

##include `<`node_api.h>

// 假设这个函数是从某处获取文件大小的
uint64_t get_large_file_size() {
    // 返回一个大于2^53的文件大小
    return (uint64_t)9007199254740993;
}

// 这是暴露给Node.js的N-API函数
napi_value GetFileSizeAsBigInt(napi_env env, napi_callback_info info) {
    uint64_t file_size = get_large_file_size();

    // 创建BigInt
    napi_value big_int_value;
    napi_status status = napi_create_bigint_uint64(env, file_size, &big_int_value);
    if (status != napi_ok) {
        // 错误处理...
    }

    return big_int_value; // 返回BigInt给JavaScript
}

然后,在你的 Node.js 代码中,你可以调用这个函数并获得一个能够安全表示大数字的 BigInt:

const nativeAddon = require("./build/Release/native-addon.node");

// 这将调用我们上面定义的C函数并返回一个BigInt
const fileSize = nativeAddon.GetFileSizeAsBigInt();

console.log(fileSize); // 输出类似于:9007199254740993n

注意,在 JavaScript 端,BigInt 值末尾会有一个 "n" 来表示它是一个 BigInt 类型,而不是普通的 Number。

napi_create_bigint_wordsopen in new window

好的,让我们来聊一下 Node.js 中的napi_create_bigint_words这个功能。

首先解释一下,什么是 N-API。N-API 是一个 C 语言的 API,它允许你使用 C 或者 C++代码编写扩展,这些扩展可以直接被 Node.js 调用。这有助于提高性能,并允许重用已有的 C/C++库。

现在,谈到napi_create_bigint_words,这个函数是 N-API 提供的一部分,专门用来创建 JavaScript 中的 BigInt 类型。在 JavaScript 中,BigInt 是一种新的数值类型,它允许你安全地存储和操作大整数,甚至是超过了 Number 类型能表示的范围。

具体到napi_create_bigint_words,这个函数使得你可以从 C/C++层面创建一个 BigInt 对象,而不是从 JavaScript 创建。这在某些情况下非常有用,比如当你在一个 node 扩展中处理大整数时,或者当你想要将一个底层的数值库结果转换为 BigInt 使其在 JavaScript 中可用时。

让我们来看一个简单的例子:

##include `<`node_api.h>

// 假设我们的任务是创建一个代表2^64的BigInt

napi_value CreateBigInt(napi_env env) {
    napi_status status;

    // BigInt由两部分组成:低位(low word)和高位(high word)
    // 通过二进制可以表示为 high_word `<``<` 64 | low_word。
    uint64_t low_word = 0; // 在我们的示例中,低位是0
    uint64_t high_word = 1; // 高位是1,因为2^64相当于1后面跟着64个0

    // 我们需要创建一个napi_value来存放我们即将创建的BigInt
    napi_value big_int_value;

    // 调用napi_create_bigint_words来创建BigInt
    // 参数解读:
    // env: 当前的环境上下文
    // sign_bit: 表示number是正还是负,0代表正数
    // word_count: 我们传递的words数量
    // words: 指向我们数值的指针
    // result: 输出参数,创建好的BigInt将被赋值到这里
    status = napi_create_bigint_words(env, 0, 1, &high_word, &big_int_value);

    // 检查是否创建成功
    if(status != napi_ok){
        napi_throw_error(env, NULL, "Unable to create BigInt");
    }

    return big_int_value;
}

在这个例子中,我们创建了一个表示 2^64 的 BigInt 对象。我们首先声明了 low_wordhigh_word,然后调用napi_create_bigint_words来实际创建 BigInt 对象。注意,我们的sign_bit设置为 0,因为我们要创建的是一个正数。

如果你在构建更复杂的 node 扩展,在处理大整数问题时,就可能会用到这个函数。例如,你可能正在封装一个金融计算库,需要处理比标准 js Number 类型更大的数字,那么你就会在 C/C++层面使用napi_create_bigint_words来创建 BigInt,然后在你的 node 应用中使用它。

napi_create_string_latin1open in new window

好的,我来解释一下 Node.js 中的napi_create_string_latin1这个函数。

在 Node.js 里,N-API 提供了一个与 JavaScript 引擎无关的原生(native)API,它允许你编写可以在不同版本的 Node.js 上运行的 C/C++扩展。napi_create_string_latin1是 N-API 中的一个功能,它允许你从 C/C++代码中创建一个新的 JavaScript 字符串。

当你在 C/C++中使用napi_create_string_latin1时,你可以将一个标准的以 null 结尾的 C 字符串(使用 Latin-1/ISO-8859-1 编码)转化成一个 JavaScript 字符串。Latin-1 编码包括英文字符和欧洲常用的其他一些字符。

这里有一些参数需要了解:

  • env: 这是一个表示 N-API 环境的句柄,它用于大多数 N-API 调用以提供 Node.js 运行时的上下文。
  • str: 是 C 字符串的指针,表示要转换成 JavaScript 字符串的原始 Latin-1 编码的字符序列。
  • length: 这是字符串的长度,如果你传递-1,函数将假定字符串是以 null 结尾的,并计算长度。
  • result: 这是一个输出参数,用于接收创建的 JavaScript 字符串。

现在举例说明: 假设你想在 C/C++扩展中创建一个表示"hello"的 JavaScript 字符串。下面是如何使用napi_create_string_latin1实现的伪代码:

##include `<`node_api.h>

// 假设你已经有了napi_env env变量

// 要转换成JavaScript字符串的C字符串
const char* c_str = "hello";
size_t str_length = 5; // 字符串"hello"的长度
napi_value result;

// 调用napi_create_string_latin1来创建一个JavaScript字符串
napi_status status = napi_create_string_latin1(env, c_str, str_length, &result);

// 检查操作是否成功
if (status == napi_ok) {
    // 如果成功,此时result就是一个JavaScript字符串
    // 可以在你的N-API函数中返回它,或者用它进行其他操作
} else {
    // 错误处理
}

在实际应用中,你可能会在一个 N-API 模块的函数内部使用这种方式来创建 JavaScript 字符串,然后将其返回给 JavaScript 层的代码。例如,如果你正在编写一个读取某个硬件设备状态并返回一个字符串描述的扩展,你可能会从该设备读取数据,然后使用napi_create_string_latin1来创建一个相应的 JavaScript 字符串来描述那个状态。

node_api_create_external_string_latin1open in new window

node_api_create_external_string_latin1 是 Node.js 中的一个 N-API 函数。N-API 是一种用于构建本地插件的 API,它提供了与底层 Node.js JavaScript 运行时进行交互的方法。此函数特别用于创建“外部”字符串,这意味着它允许你将现有的字符数据作为 Node.js 中的 JavaScript 字符串使用,而不必复制数据。

在详细解释之前,先来理解几个概念:

  • N-API: Node.js 的一个稳定、版本无关的 API,用于构建原生插件。
  • 外部字符串(External string): JavaScript 中的字符串,其内存是由开发者管理的,而非由 JavaScript 引擎管理。
  • Latin1: 也称为 ISO-8859-1,是一个 8 位的单字节编码方案,可以表示西欧语系中的大多数字符。

功能

node_api_create_external_string_latin1 允许你把已经存在的 Latin1 编码的字符数组创建成一个 Node.js 的字符串。这样做的好处是避免了将现有字符串复制到新的内存位置,节省了时间和空间。

参数

这个函数接收以下参数:

  • env: 当前执行环境的句柄,提供给所有 N-API 调用。
  • data: 指向你想要转换成 JavaScript 字符串的 Latin1 编码数据的指针。
  • length: 数据的长度,单位是字节。
  • result: 用来存放创建好的 JavaScript 字符串的变量。

使用场景举例

假设你在 C/C++扩展模块中有一个以 Latin1 编码的字符串,并希望将它传递给 Node.js 中的 JavaScript 代码使用:

##include `<`node_api.h>

// 假设你有一个Latin1编码的字符串数据
const char* latin1_string = "Hello, World!";

// 这个函数会被调用来创建一个Node.js的String
napi_value CreateLatin1String(napi_env env) {
  napi_value result;

  // 把外部Latin1编码的字符串转换为Node.js字符串
  napi_status status = node_api_create_external_string_latin1(
    env,
    latin1_string,
    NAPI_AUTO_LENGTH, // 自动计算字符串长度
    &result
  );

  if (status != napi_ok) {
    // 处理错误情况...
  }

  return result; // 返回创建的字符串
}

// ... 接下来,你需要将上面的函数暴露给Node.js ...

在上面的代码示例中,我们定义了一个名为 CreateLatin1String 的函数,它使用 node_api_create_external_string_latin1 来创建一个 Node.js 的字符串。我们传入了一个指向 Latin1 编码字符串的指针,告诉函数自动计算字符串的长度,并且提供了一个用于存放结果字符串的变量。成功调用后,result 将包含一个 Node.js 字符串,可以像任何其他字符串那样在 JavaScript 中使用。

注意事项

使用外部字符串时,需要确保在字符串仍然被使用时不要释放或修改底层的内存块。如果底层的数据被改变或删除,那么 JavaScript 中的字符串也会受到影响,可能导致程序崩溃或出现不可预测的行为。

总之,node_api_create_external_string_latin1 是一个强大的功能,它能够高效地将本地代码中的字符串数据转换为 JavaScript 字符串,但它需要开发者在内存管理方面进行额外的注意。

napi_create_string_utf16open in new window

当然,我来帮你解释一下 napi_create_string_utf16 这个函数。

在 Node.js 中,napi_create_string_utf16 是 N-API 的一部分,而 N-API 是一个 C 语言的接口,让你可以创建本地插件。这些插件是用 C 或 C++写的,它们可以直接和 Node.js 的运行时进行交互,通常用于性能敏感的操作或者是调用系统级别、硬件相关的库。

napi_create_string_utf16 函数用于在原生代码中创建一个 UTF-16 编码的 JavaScript 字符串。UTF-16 是一种字符编码方式,它能表示 Unicode 标准中的大部分字符,并且在 JavaScript 内部是默认的字符串编码。

下面是这个函数具体的描述:

napi_status napi_create_string_utf16(napi_env env,
                                     const char16_t* str,
                                     size_t length,
                                     napi_value* result);

函数参数解释:

  • env: 当前 native API 的环境句柄,它是操作 N-API 的上下文。
  • str: 指向 UTF-16 编码字符串的指针。
  • length: 字符串的长度,用字符数表示。如果长度设置为 -1,则假定字符串以 null 终止符结束。
  • result: 是一个出参,用于返回创建的 JavaScript 字符串。

函数会返回一个napi_status枚举值,表示操作成功与否。如果函数执行成功,result将包含对应的 JavaScript 字符串值。

例子:

假设我们要在本地插件中创建一个 JavaScript 字符串 "Hello, World!" 并返回给 Node.js。首先,我们需要将这个字符串转换成 UTF-16 编码。

##include `<`node_api.h>

napi_value CreateUtf16String(napi_env env, napi_callback_info info) {
  // UTF-16编码的 "Hello, World!" 字符串
  // 在C语言中,UTF-16字符串通常使用 char16_t 类型表示
  const char16_t str[] = u"Hello, World!";

  // 我们知道这个字符串的长度,所以我们可以直接传入,
  // 如果不知道,也可以通过类似于计算null终止符的方式来获取长度
  size_t length = sizeof(str) / sizeof(char16_t) - 1; // 减去null终止符

  napi_value result;
  // 调用函数创建JS字符串
  napi_status status = napi_create_string_utf16(env, str, length, &result);

  // 检查是否成功
  if (status != napi_ok) {
    // 处理错误情况...
  }

  // 返回创建的字符串
  return result;
}

// ... 其他代码用于注册该函数,使得它可以从Node.js中调用 ...

在 Node.js 代码中,你可能会调用这个本地插件提供的函数像这样:

const nativeAddon = require("./path_to_native_addon");
console.log(nativeAddon.CreateUtf16String()); // 应该打印出 "Hello, World!"

这里我们在 C 代码中创建了一个 Javascript 字符串,然后在 Node.js 中打印出来。这只是一个简单的例子展示了如何利用napi_create_string_utf16函数在原生模块中创建和返回 JavaScript 字符串。

node_api_create_external_string_utf16open in new window

node_api_create_external_string_utf16 函数是 Node.js 中 N-API 提供的一个功能,它允许你在原生插件中创建一个新的 JavaScript 字符串,而这个字符串的内容实际上存储在外部分配的内存中(也就是说,不是由 V8 管理的内存)。使用这个函数可以帮助你高效地在原生代码和 JavaScript 之间传递大量文本数据,而无需复制字符串数据。

在了解如何使用 node_api_create_external_string_utf16 前,你需要知道几个基本概念:

  1. UTF-16:这是一种编码方式,用于将字符表示为数字代码。UTF-16 可以编码世界上绝大多数的字符,并且每个字符使用 2 个或者更多的字节来表示。

  2. N-API:是 Node.js 提供的一个稳定的 API 层,让你可以构建原生插件,这样的插件可以直接运行在 Node.js 的底层引擎 V8 之上。

  3. 原生插件:是使用 C 或 C++ 编写的模块,它们可以通过 Node.js 的 N-API 被加载和执行,提供比纯 JavaScript 更高效的性能,尤其是在处理大量数据时。

下面是 node_api_create_external_string_utf16 函数的基本用法示例和解释:

##include `<`node_api.h>

// 假设我们有一个 UTF-16 编码的字符串,存在于外部分配的内存中
const char16_t* external_data = u"Hello, World!";

// 这个函数会被用来在后续清理外部分配的内存
void MyFinalizer(napi_env env, void* finalize_data, void* finalize_hint) {
    free(finalize_data);
}

napi_value CreateExternalStringUtf16(napi_env env) {
    napi_value result;

    // 创建一个 JavaScript 字符串,其内容链接到外部的 UTF-16 编码数据
    napi_status status = napi_create_external_string_utf16(
        env,
        external_data,
        NAPI_AUTO_LENGTH,   // 让 N-API 自动计算字符串长度
        &result
    );

    // 检查是否正确执行
    if (status != napi_ok) {
        // 处理错误...
    }

    // 返回创建的 JavaScript 字符串
    return result;
}

在上面的代码中,我们首先包含了 node_api.h 头文件,这是使用 N-API 必须做的。然后,我们声明了一个 UTF-16 编码的字符串 external_data 和一个清理函数 MyFinalizer,当 JavaScript 字符串不再被需要时,MyFinalizer 会被调用来释放外部分配的内存。

CreateExternalStringUtf16 函数是创建字符串的实际工作地方。我们调用了 napi_create_external_string_utf16,向它传递环境句柄 env、指向外部数据的指针 external_data、使用 NAPI_AUTO_LENGTH 让 API 自动计算字符串长度,最后传入一个指向 napi_value 变量的指针来接收创建出的 JavaScript 字符串。

注意,在实际应用中,你需要确保传递给 napi_create_external_string_utf16 的内存在字符串不再使用后得到释放,否则会产生内存泄漏。这通常是通过注册一个“finalizer”函数完成的,就像示例中的 MyFinalizer 那样。

通过这样的机制,Node.js 的 N-API 允许你在原生插件中处理字符串时,既可以达到高效率,又能够防止内存泄漏。

napi_create_string_utf8open in new window

napi_create_string_utf8 是 Node.js 中的一个 N-API 函数。N-API(Node API)是 Node.js 提供的一套 C 语言的 API,使得原生插件(通常用 C 或 C++编写)能够与 JavaScript 代码相互交互。这样,开发者就可以在 Node.js 环境中使用 C 或 C++ 编写高性能的本地插件。

解释 napi_create_string_utf8

napi_create_string_utf8 的作用是创建一个新的 UTF-8 编码的字符串,并将其表示为 N-API 中的 napi_value 类型。该函数允许你把一个 C 语言中的字符串(char* 类型),转换成可以在 Node.js 代码中作为 JavaScript 字符串使用的值。

参数

该函数通常接收以下几个参数:

  1. env: 这是一个 napi_env 句柄,代表当前的 Node.js 环境。它是执行 N-API 调用的上下文。
  2. str: 指向以 null 结尾的 UTF-8 编码的字符串的指针。
  3. length: 字符串的长度(以字节为单位)。如果长度未知,可以传递 -1,函数会自动计算字符串长度。
  4. result: 指向 napi_value 的指针,用来接收创建好的 JavaScript 字符串。

返回值

该函数返回一个 napi_status 枚举值,表示操作成功或失败的状态。

代码示例

假设你已经有了一个 C 插件,并想要创建一个 JavaScript 字符串并返回给 JavaScript 端,以下是如何使用 napi_create_string_utf8 的例子:

##include `<`node_api.h>

// 示例函数,用于创建字符串返回
napi_value CreateString(napi_env env, napi_callback_info info) {
    napi_value str;
    // 创建一个 UTF-8 编码的 JavaScript 字符串 "Hello, World!"
    napi_status status = napi_create_string_utf8(env, "Hello, World!", NAPI_AUTO_LENGTH, &str);

    // 检查是否成功创建字符串
    if (status != napi_ok) {
        // 处理错误
        // 在实际应用中,你可能需要调用另一个 N-API 函数来抛出错误
        return nullptr;
    }

    // 如果成功,返回创建的字符串
    return str;
}

// 初始化函数,注册上面的示例函数到 Node.js
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;

    // 将 CreateString 包装为 JavaScript 可调用的函数
    napi_create_function(env, NULL, 0, CreateString, NULL, &fn);

    // 将这个函数作为模块导出
    napi_set_named_property(env, exports, "createString", fn);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

这段代码创建了一个简单的函数 CreateString,这个函数可以在 JavaScript 中被调用,返回一个 "Hello, World!" 字符串。NAPI_MODULE 宏用于注册初始化函数,确保在模块载入时运行 Init 函数,这样 createString 就可以导出并在 Node.js 代码中使用了。

在 JavaScript 端,你可以像下面这样调用 createString 函数:

const addon = require("./build/Release/addon");

console.log(addon.createString()); // 打印 "Hello, World!"

请注意,在实际应用中,处理 napi_status 返回值和错误检查非常重要,因为他们可以帮助识别问题所在。此外,在引入和使用原生模块前,需要先编译它们,通常是使用 node-gyp 这种工具。

node_api_create_property_key_utf16open in new window

node_api_create_property_key_utf16 是 Node.js 中的一个函数,它属于 N-API 的一部分。N-API(Node API)是一个用来构建原生插件的 API 层,允许你用 C 或 C++编写可以与 Node.js JavaScript 代码直接互操作的模块。

在 JavaScript 中,对象通常由键值对组成,即每个属性都有一个名称(键)和与之关联的值。在使用原生代码与 JavaScript 交互时,我们需要能够创建这些键,以便我们可以读取和设置 JavaScript 对象上的属性。

函数 node_api_create_property_key_utf16 具体用途是:创建一个 UTF-16 编码的字符串作为属性名,并将其注册到 JavaScript 运行时环境中,使得原生模块可以使用这个字符串作为对象的属性键。

详细解释:

  • UTF-16 是 Unicode 字符集的一种编码方式,它采用 2 个字节或者更多来表示一个字符。
  • 当你想要从原生模块(例如 C/C++模块)中定义一个新的属性并且给 JavaScript 对象赋值时,你需要使用这个函数来创建属性名。

实际应用例子: 假设你正在编写一个原生模块,该模块需要给 JavaScript 中的某个对象添加一个名为“exampleProperty”的新属性。你可以通过以下步骤使用 node_api_create_property_key_utf16

  1. 在你的 C/C++代码中,你首先定义一个 UTF-16 编码的字符串,表示你要创建的属性名。
  2. 然后,你调用 node_api_create_property_key_utf16 函数,传入你的 N-API 环境、UTF-16 字符串及其长度,该函数会返回一个用于标识这个属性的 N-API 值。
  3. 最后,你可以使用这个属性键在 JavaScript 对象上设置或获取对应的属性值。

下面是一个简化的代码示例,演示如何使用 node_api_create_property_key_utf16 创建属性键:

##include `<`node_api.h>

napi_value CreateExampleProperty(napi_env env, napi_callback_info info) {
  // 定义UTF-16字符串,代表你想要的属性名称,比如“exampleProperty”。
  const char16_t property_name[] = u"exampleProperty";

  // 创建一个属性键变量。
  napi_value property_key;

  // 调用函数创建属性键。
  napi_status status = napi_create_property_key_utf16(env,
                                                      property_name,
                                                      sizeof(property_name) - 1, // 减去字符串结束符 \0 的长度
                                                      &property_key);

  // 检查是否成功创建了属性键。
  if (status != napi_ok) {
    // 处理错误情况...
  }

  // 这里假设 "object" 是已经在其他地方创建好的JavaScript对象。
  napi_value object;

  // 使用属性键在对象上设置属性。
  // 这里只是示例流程,你还需要创建实际的值(value)并设置到对象上。
  napi_value value;
  // ...创建或获取value...

  status = napi_set_property(env, object, property_key, value);

  if (status != napi_ok) {
    // 处理错误情况...
  }

  // 返回新创建的属性值,或者其他什么东西。
  return value;
}

注意,在实际编写的过程中,还需要考虑错误处理和确保正确调用 N-API 提供的其他函数进行完整的功能实现。上述代码仅展示了如何使用node_api_create_property_key_utf16函数。

Functions to convert from Node-API to C typesopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境。它允许你在服务器端运行 JavaScript 代码。Node-API(之前称为 N-API)是 Node.js 的一个功能,它提供了一组 API,使得原生模块(通常用 C 或 C++编写)能够与 JavaScript 代码进行互操作。

在 Node-API 中,“Functions to convert from Node-API to C types”意味着有一组特定的函数可以帮助开发者将从 JavaScript 接收的数据转换成 C 语言中的数据类型,以便在 C/C++ 编写的原生模块中使用。

这些函数通常涉及以下几个方面:

  1. 转换数字:比如把 JavaScript 中的 Number 转换为 C 中的 int, float, double 等。
  2. 转换字符串:将 JavaScript 字符串转换成 C 风格的字符串(即以 null 结尾的字符数组)。
  3. 转换对象:如果 JavaScript 对象需要在 C 代码中处理,可能需要转换为对应的 C 结构体。
  4. 转换函数:将 JavaScript 中的函数转换为可以在 C 代码中调用的回调。

现在,让我们通过一些例子来说明如何使用 Node-API 进行类型转换。

示例 1:转换数字

假设您的 JavaScript 代码传递了一个数字给原生模块,并且您想要在 C 代码中以 double 类型使用它。

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value argv[1];
    size_t argc = 1;
    double number;

    // 获取 JavaScript 传入的参数
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    // 将第一个参数转换为 C 的 double 类型
    status = napi_get_value_double(env, argv[0], &number);

    // 现在你可以在 C 代码中使用变量 number 了
    // ...

    // 返回结果到 JavaScript
    napi_value result;
    status = napi_create_double(env, number * 2, &result);
    return result;
}

示例 2:转换字符串

假设您接收到了一个 JavaScript 字符串,并希望在原生模块中作为 C 字符串处理。

##include `<`node_api.h>
#include `<`string.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value argv[1];
    size_t argc = 1;
    size_t str_length, str_length_in_bytes;

    // 获取 JavaScript 传入的参数
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    // 第一步,获取字符串长度
    status = napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_length_in_bytes);

    // 准备足够大小的缓冲区来存储 C 字符串
    char* c_str = (char*)malloc(str_length_in_bytes + 1);

    // 第二步,实际复制字符串内容
    status = napi_get_value_string_utf8(env, argv[0], c_str, str_length_in_bytes + 1, &str_length);

    // 现在您可以使用 c_str 作为 C 字符串
    // ...

    // 清理分配的内存
    free(c_str);

    // 处理结束后返回 undefined 给 JavaScript
    napi_value result;
    status = napi_get_undefined(env, &result);
    return result;
}

示例 3:转换函数(回调)

如果 JavaScript 传递了一个函数作为回调给原生模块,我们可能希望在某个事件发生时调用它。

##include `<`node_api.h>

void CallJavaScriptCallback(napi_env env, napi_value js_callback) {
    napi_status status;

    // 假设我们想要回调 JavaScript 函数并传递一个参数给它
    napi_value arg;
    status = napi_create_string_utf8(env, "Hello from native!", NAPI_AUTO_LENGTH, &arg);

    napi_value global;
    status = napi_get_global(env, &global);

    napi_value result;
    status = napi_call_function(env, global, js_callback, 1, &arg, &result);

    // 回调函数已调用
}

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value argv[1];
    size_t argc = 1;

    // 获取 JavaScript 传入的参数
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    // 确保传入的参数是一个函数
    napi_valuetype valuetype;
    status = napi_typeof(env, argv[0], &valuetype);
    if (valuetype != napi_function) {
        // 抛出错误
    }

    // 存储或直接使用 JavaScript 提供的回调函数
    CallJavaScriptCallback(env, argv[0]);

    // 处理结束后返回 undefined 给 JavaScript
    napi_value result;
    status = napi_get_undefined(env, &result);
    return result;
}

这些例子演示了如何使用 Node-API 提供的函数将 JavaScript 中的数据类型转换为适合 C/C++ 使用的数据类型。这是创建原生插件和模块时非常重要的一个步骤,因为这样才能确保数据在不同语言间正确无误地传递和处理。

napi_get_array_lengthopen in new window

好的,Node.js 的 N-API 是一个用于构建原生插件的接口。N-API 旨在提供一个与 JavaScript 运行时无关的抽象层,这样您编写的代码就可以在不同版本的 Node.js 中运行,而无需重新编译。

napi_get_array_length 函数是 N-API 提供的一个功能,它允许你在原生模块中获取一个 JavaScript 数组的长度。当你在 C 或 C++ 编写的扩展中与 JavaScript 数组打交道时,你可能需要知道数组有多少项,这个函数正是用来帮助你这么做的。

这里有一个简单的例子说明 napi_get_array_length 是如何工作的:

假设你有一个 JavaScript 数组,并且你想通过一个原生插件来处理这个数组。在原生插件的代码中,你将获得一个 napi_value 类型的引用,指向这个数组。要获取该数组的长度,你将使用 napi_get_array_length 函数。

##include `<`node_api.h>

// 函数用于获取数组长度
napi_status GetArrayLength(napi_env env, napi_value array, uint32_t* result) {
    return napi_get_array_length(env, array, result);
}

在这个示例中:

  • env 是一个 napi_env 句柄,它代表了 N-API 调用上下文。
  • array 是一个 napi_value,表示你想获取长度的 JavaScript 数组。
  • result 是一个指向 uint32_t 变量的指针,在调用此函数后,它将被设置为数组的长度。

假设你已经在 JavaScript 中创建了一个数组并传递给了这个原生函数:

let myArray = [1, 2, 3, 4, 5];

当你在原生代码中调用 GetArrayLength 并将 myArray 作为参数传递时,result 将会被设置为 5,因为 myArray 包含五个元素。

请注意,这只是展示 napi_get_array_length 功能的一个简化的例子,实际上在原生模块中使用时,你还需要进行错误检查和合适的异常处理来确保代码的健壮性。

napi_get_arraybuffer_infoopen in new window

napi_get_arraybuffer_info 是 Node.js 中的 Native API(N-API)的一部分,它允许原生插件与 JavaScript 代码交互。Node.js 的 N-API 是一个用于构建原生插件的稳定层,使得开发者可以不受 Node.js 版本更迭影响的情况下编写和维护扩展模块。

在解释 napi_get_arraybuffer_info 之前,我们需要了解一下 JavaScript 中的 ArrayBuffer 对象。ArrayBuffer 是一种用来表示一个固定长度的二进制数据缓冲区的对象,在这个缓冲区中,你可以存储任何类型的二进制数据。这对于处理像文件、图像或其他二进制流的数据是非常有用的。

现在,让我们来详细看看 napi_get_arraybuffer_info 这个函数做什么:

当你使用 N-API 编写原生代码时(比如 C 或者 C++),并且需要操作或访问由 JavaScript 代码创建的 ArrayBuffer 对象时,你就会用到 napi_get_arraybuffer_info 这个函数。这个函数帮助你从一个给定的 N-API 值中检索 ArrayBuffer 相关的信息,并提供对实际的二进制数据的直接访问权限。

函数原型大概是这样的:

napi_status napi_get_arraybuffer_info(
    napi_env env,
    napi_value arraybuffer,
    void** data,
    size_t* length);

参数说明:

  • env: 当前环境的句柄,它是 N-API 函数进行交互的上下文。
  • arraybuffer: 一个 N-API 值,代表 JavaScript 中的 ArrayBuffer 对象。
  • data: 这是一个输出参数,用来接收缓冲区的指针,即 ArrayBuffer 中数据的开始地址。
  • length: 这也是一个输出参数,用来接收 ArrayBuffer 的长度(字节为单位)。

返回值:

  • 这个函数返回一个 napi_status 枚举,表示函数调用是否成功。如果返回 napi_ok,则表示没有错误。

实战例子

假设我们正在编写一个 Node.js 插件,该插件需要读取通过 JavaScript 传递过来的二进制图像数据,并且我们想要获取这些数据以及它们的大小,那么 napi_get_arraybuffer_info 就派上了用场。

首先,JavaScript 端可能有以下代码来创建一个 ArrayBuffer,并将其传递给原生模块:

const fs = require("fs");

// 假设我们有一个图像文件
let imageBuffer = fs.readFileSync("path/to/image.png");

// 创建 ArrayBuffer
let arrayBuffer = new ArrayBuffer(imageBuffer.length);

// 将 Node.js Buffer 数据复制到 ArrayBuffer
new Uint8Array(arrayBuffer).set(new Uint8Array(imageBuffer));

// 调用原生模块处理图像
nativeModule.processImage(arrayBuffer);

然后在原生模块中,我们可能会这样使用 napi_get_arraybuffer_info

##include `<`node_api.h>

// 一个 N-API 应用程序中由 JavaScript 调用的函数
napi_value ProcessImage(napi_env env, napi_callback_info info) {
    napi_status status;

    size_t argc = 1;
    napi_value args[1];
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 确保我们获得了正确的参数数目
    if (status != napi_ok || argc `<` 1) {
        // 处理错误...
    }

    void* bufferData;
    size_t bufferLength;
    status = napi_get_arraybuffer_info(env, args[0], &bufferData, &bufferLength);

    // 确保 ArrayBuffer 成功被获取
    if (status == napi_ok) {
        // 在这里,我们可以使用 bufferData 和 bufferLength 来操作图像数据

    } else {
        // 处理错误...
    }

    // 返回一些值或 `undefined`
    napi_value result;
    status = napi_get_undefined(env, &result);
    return result;
}

// 模块初始化代码,注册上述函数...

在这段代码中,我们定义了一个 ProcessImage 函数,这个函数首先从 JavaScript 传递的参数中获取 ArrayBuffer 对象,然后用 napi_get_arraybuffer_info 来获取指向实际数据的指针和数据的长度。这样一来,我们就可以在原生代码中直接操作这些数据了。

napi_get_buffer_infoopen in new window

当然,我会详细地解释 napi_get_buffer_info 这个函数。首先,让我们了解下什么是 N-API 和 Node.js 中的 Buffer。

N-API 是 Node.js 的一个 C API,它用于创建本地插件。这意味着如果你想在 Node.js 中使用 C/C++编写的代码,你可能会使用到 N-API。Buffer 在 Node.js 中通常被用来处理二进制数据流,比如读取文件、网络通信等场景。

现在,谈谈 napi_get_buffer_info 函数。这个函数是 N-API 提供的一个接口,它允许你从一个 JavaScript Buffer 对象中获取原始的内存信息。内存信息指的是数据存储的位置(一个指向内存的指针)和数据的长度。

napi_get_buffer_info 的具体作用是:

  • 它帮助你从 Node.js 的 Buffer 对象获取到底层的字节数据和大小。
  • 它通常在编写本地插件时使用,当需要直接操作或访问 Buffer 对象内部数据时。

函数的签名大致如下:

napi_status napi_get_buffer_info(
  napi_env env,            // [in] N-API环境句柄
  napi_value value,        // [in] 要检索信息的Buffer对象
  void** data,             // [out] 指向Buffer数据的指针的指针
  size_t* length           // [out] Buffer数据长度的指针
);

举个例子,假设你有一个 Node.js Buffer 对象,你正在编写一个本地插件,并且想要得到这个 Buffer 的数据和它的长度,以便进行一些底层的操作。

你可以这样使用 napi_get_buffer_info

  1. 创建一个 Buffer 对象,在 JavaScript 环境中。
  2. 在你的 C/C++ 插件代码中,调用 napi_get_buffer_info
  3. napi_get_buffer_info 将返回指向 Buffer 数据的指针和数据的长度。
  4. 使用这些信息,你可以在你的插件中对数据进行处理。
##include `<`node_api.h>

// 假设 args[0] 是传递给本地函数的 Buffer 对象
napi_value SomeNativeFunction(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1];
  napi_get_cb_info(env, info, &argc, args, NULL, NULL);

  void* buffer_data;
  size_t buffer_length;
  napi_get_buffer_info(env, args[0], &buffer_data, &buffer_length);

  // 现在我们拿到了 buffer_data 指针和 buffer_length,
  // 可以进行后续操作,比如读取数据或者修改数据。

  // ...

  return NULL; // 根据情况返回相应的值或对象
}

通过这段代码,你可以操作 JS 层面的 Buffer 对象在内存中的数据。记住,直接操作内存数据可以提高性能,因为避免了不必要的复制,但同时也需要小心处理,防止破坏数据结构,引发安全问题或造成内存泄漏。

napi_get_buffer_info 是 Node.js 中用于本地插件开发的重要工具,特别是当处理性能敏感型操作或需要将数据在 Node.js 和本地代码之间传递时。

napi_get_prototypeopen in new window

当然,很乐意为你解释。N-API(Node.js API)是一个用来构建原生插件的 API。在 Node.js 中,原生插件指的是用 C 或者 C++编写的模块,这些模块可以直接调用 Node.js 底层 API 以及 V8(Node.js 的 JavaScript 引擎)提供的功能。

napi_get_prototype是 N-API 的一个函数,它允许你取得某个 JavaScript 对象的原型。"原型"是 JavaScript 中的一个重要概念,几乎所有的 JavaScript 对象都有一个与之关联的原型对象,该原型对象定义了一系列可被继承的属性和方法。

在 JavaScript 中,通常使用object.__proto__或者Object.getPrototypeOf(object)获取对象的原型。但是在 C/C++编码的原生模块中,我们需要使用 N-API 提供的函数,比如napi_get_prototype,来实现相同的操作。

以下是napi_get_prototype函数的简单说明:

napi_status napi_get_prototype(napi_env env, napi_value js_object, napi_value* result);
  • env: 这是表示当前 N-API 环境的句柄,它是与当前回调相关联的上下文。
  • js_object: 这是你想要获取原型的 JavaScript 对象。
  • result: 这是一个指向napi_value的指针,函数执行成功后,它会指向输入对象的原型。

如果函数调用成功,返回值会是napi_ok,表明没有错误发生。

现在让我们通过一个假设的例子来看napi_get_prototype是如何工作的:

假设在 JavaScript 中,你有一个基于类Animal创建的实例dog。在这个类中,有一个方法叫makeSound

class Animal {
  makeSound() {
    console.log("Some sound");
  }
}

const dog = new Animal();

在一个原生 Node.js 模块中,你可能想要获取这个dog对象的原型,并检查是否存在makeSound方法。你可以这样做:

  1. 你首先使用napi_create_referencedog对象包裹起来,使其可以在原生代码中安全地使用。
  2. 然后,你调用napi_get_prototype来获取dog对象的原型。
  3. 最后,你可能会使用napi_has_property来检查这个原型对象上是否有makeSound这个方法。

在 C/C++代码中大致的步骤如下:

// 假设你已经从某处获得了 'dog' 对象的 napi_value 表示.

napi_value dog_prototype;
napi_status status = napi_get_prototype(env, dog, &dog_prototype);
if (status != napi_ok) {
    // 处理错误...
}

bool has_makeSound;
status = napi_has_property(env, dog_prototype, "makeSound", &has_makeSound);
if (status != napi_ok) {
    // 处理错误...
}

// 现在 you can do something with the information whether the prototype has makeSound method.
if (has_makeSound) {
    // Prototype has the makeSound method.
} else {
    // Prototype does not have the makeSound method.
}

请注意,这只是一个抽象的例子,实际上你可能还需要进行更多的错误检查和资源管理。希望这个解释对你有所帮助,让你更好地理解napi_get_prototype的作用和如何在原生 Node.js 模块中使用它。

napi_get_typedarray_infoopen in new window

Node.js 中的 N-API 是一个底层的 API,允许你用 C 语言编写的代码与 JavaScript 代码进行交互,这在创建或使用原生插件时非常有用。napi_get_typedarray_info是 N-API 提供的一个函数,让你能够从 C 语言的角度获取 JavaScript 中的 TypedArray(类型化数组)的相关信息。

在 JavaScript 中,TypedArray 是一种特殊的数组,它允许你用一个固定类型的数据来创建一个数组。例如,一个Float32Array就是只包含 32 位浮点数的数组。这对于需要高性能处理如音频数据、图像处理等场景非常有用。

现在,如果你在一个原生模块中需要操作一个从 JavaScript 传递过来的 TypedArray,你就可以使用napi_get_typedarray_info函数来了解这个 TypedArray 的详细信息,比如它的类型、长度、以及内存中的位置。

下面是napi_get_typedarray_info的基本使用方法:

// 假设 napi_env env 和 napi_value typed_array 是已经提供的参数。

napi_status status;
napi_typedarray_type type;
size_t length;
void* data;
size_t byte_offset;
size_t byte_length;

status = napi_get_typedarray_info(
  env,           // N-API环境句柄
  typed_array,   // JavaScript中的类型化数组
  &type,         // 这里将返回类型化数组的类型
  &length,       // 这里将返回元素的个数
  &data,         // 这里将返回指向数据的指针
  NULL,          // 如果不关心 ArrayBuffer,这里可以传 NULL
  &byte_offset,  // 数据在 ArrayBuffer 中的字节偏移量
  &byte_length   // 数据的字节长度
);

if (status == napi_ok) {
  // 现在你可以根据 type 来知道数组中的数据类型,
  // 使用 data 指针访问数组中的实际数据,
  // 并通过 length 获取数组中元素的数量。
}

下面是几个实际的运用例子:

  1. 处理图形数据:假设你正在编写一个 Node.js 的图像处理模块,JavaScript 传递了一个包含图像像素数据的Uint8Array给你的原生模块。你可以用napi_get_typedarray_info来获取这个数组的具体信息,并进行图像处理算法的应用,如滤镜、颜色调整等。

  2. 音频编码/解码:如果你的原生模块是用来处理音频数据的,JavaScript 可能会传递一个Float32Array包含音频样本。你可以用napi_get_typedarray_info获得所需信息并对音频数据进行编码或解码处理。

  3. 科学计算:科学计算往往需要处理大量的数值数据,JavaScript 端可能会传输Float64Array类型的 TypedArray 作为数据集。你的原生模块可以使用napi_get_typedarray_info来访问这些数据执行复杂的数学计算,如矩阵运算、统计分析等。

napi_get_dataview_infoopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API,它允许 JavaScript 代码和 C/C++代码之间进行交互。这样你可以在 Node.js 程序中使用 C/C++编写的高性能代码。napi_get_dataview_info是 N-API 的一部分,它用来获取DataView对象的详细信息。

那么,什么是 DataView 呢?在 JavaScript 中,ArrayBuffer是一种存储二进制数据的方式,而DataView提供了一个低级别的接口来读取和写入ArrayBuffer的不同类型的数据。

函数napi_get_dataview_info会告诉我们一个DataView对象里面有关于ArrayBuffer的哪些信息:

  • byte_offset:这是DataView开始读数据的地方在ArrayBuffer中的偏移量。
  • byte_length:这是DataView可以读数据的长度。
  • arraybuffer:这实际上就是底层的ArrayBuffer对象。

现在,让我们通过例子来说明这个函数是怎么工作的。

假设你在 C/C++扩展模块中有以下的 N-API 函数:

##include `<`node_api.h>

// 假设某个已经绑定好的函数,当从JS调用时候执行
napi_value GetDataviewInfo(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 获取DataView相关信息
    void* data;
    size_t byte_offset;
    size_t byte_length;
    napi_value arraybuffer;
    napi_status status;

    status = napi_get_dataview_info(
        env,
        args[0], // 这里args[0]是从JS传进来的DataView对象
        &byte_length,
        &data,
        &arraybuffer,
        &byte_offset
    );

    // 确保调用成功
    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Unable to get DataView information");
        return NULL;
    }

    // 根据获取到的信息可以做进一步处理,比如读取或者写入数据

    // 返回结果给JS...
}

// 初始化函数,注册GetDataviewInfo到exports对象
NAPI_MODULE_INIT() {
    napi_value dataview_info_func;
    napi_create_function(env, NULL, 0, GetDataviewInfo, NULL, &dataview_info_func);
    napi_set_named_property(env, exports, "getDataviewInfo", dataview_info_func);
    return exports;
}

在 JavaScript 中,你可能会这样使用这个扩展模块来获取DataView的信息:

const myAddon = require("./build/Release/my-addon.node");

let buffer = new ArrayBuffer(16);
let view = new DataView(buffer, 0, 16);

// 调用我们的C/C++扩展函数来获取DataView的信息
let info = myAddon.getDataviewInfo(view);
console.log(info); // 这里将打印出DataView的偏移量、长度等信息

这个例子显示了如何在 Node.js 的 N-API 中使用napi_get_dataview_info函数来从 JavaScript 传递的DataView对象中提取信息。这在你需要在本地扩展模块中处理二进制数据时非常有用。

napi_get_date_valueopen in new window

好的,我来解释一下 napi_get_date_value 函数在 Node.js 中的作用及其运用。

首先我们需要知道 N-API 是什么。简单来说,N-API 是 Node.js 提供的一个 C 语言 API 层,它允许你用 C 或者 C++编写代码来构建原生插件。原生插件是指直接与底层操作系统进行交互的模块,这通常用于性能关键的操作或者调用系统级别的 API。

现在,介绍 napi_get_date_value 函数:

这个函数的目的是从一个 JavaScript Date 对象中提取出表示日期和时间的数值。在 JavaScript 中,日期和时间被存储为自 1970 年 1 月 1 日 UTC(协调世界时)以来的毫秒数,这也被称作时间戳。

napi_get_date_value 函数作为 N-API 的一部分,允许你在编写扩展代码时(比如用 C/C++),获取 JavaScript Date 对象所代表的时间戳。

函数的定义大致如下:

napi_status napi_get_date_value(napi_env env,
                                napi_value value,
                                double* result);
  • env:当前执行环境的句柄。
  • value:一个 napi_value 类型的变量,它应该是一个 JavaScript Date 对象。
  • result:一个指向 double 类型变量的指针,函数会将 Date 对象的时间戳以毫秒为单位存储在这个变量里。

如果函数成功执行,它会返回 napi_ok

现在让我们举一个例子:

假设你正在编写一个 Node.js 的原生插件,这个插件需要处理 JavaScript 传递过来的日期数据,然后进行一些复杂的操作,例如与其他系统的日期/时间交互,或者执行一些性能密集型的日期计算。

在 C++代码中,你可能会这样使用 napi_get_date_value

##include `<`node_api.h>

// 这个函数被假定为N-API暴露给JavaScript的函数
napi_value ProcessDate(napi_env env, napi_callback_info info) {
  napi_status status;

  // 获取JavaScript传递的参数
  size_t argc = 1;
  napi_value args[1];
  status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

  // 确保获得了一个参数,并且该参数是一个Date对象
  if (status != napi_ok || argc `<` 1) {
    // Handle error...
  }

  // 提取Date对象的时间戳
  double dateValue;
  status = napi_get_date_value(env, args[0], &dateValue);
  if (status != napi_ok) {
    // Handle error...
  }

  // 在这里,你可以利用dateValue变量(它包含了时间戳)来执行你的操作...

  // 做完所有处理后,返回结果到JavaScript
  napi_value result;
  status = napi_create_double(env, dateValue, &result);
  if (status != napi_ok) {
    // Handle error...
  }

  return result;
}

以上代码展示了如何在一个 N-API 函数内部获取 JavaScript 传递的 Date 对象参数,并将其转换为表示时间戳的数字,然后用这个值做进一步的处理。

在 JavaScript 端的调用可能会是这样的:

const nativeAddon = require("./build/Release/native-addon.node");

const jsDate = new Date("2023-01-01T00:00:00Z");
const timestamp = nativeAddon.ProcessDate(jsDate);
console.log(timestamp); // 输出对应的时间戳

这里 native-addon 是我们假设的原生模块名字,它通过 ProcessDate 函数暴露给 JavaScript,JavaScript 端创建了一个 Date 实例并传递给了这个函数,然后得到了相应的时间戳输出。

希望这个解释和例子能够帮助你理解 napi_get_date_value 函数在 Node.js 中的用法。

napi_get_value_boolopen in new window

好的,我来解释一下 Node.js 中 napi_get_value_bool 这个 API 的作用和应用场景。

首先,napi_get_value_bool 是 Node.js 的 N-API 接口中的一个函数,它用于将一个 JavaScript 值从 JavaScript 环境转换成 C 或者 C++ 环境中的原生布尔值类型(即 bool 类型)。

在 JavaScript 中,有五种数据类型被称为 "Falsy" 值,它们包括:false, 0, -0, "" (空字符串), null, undefined, 和 NaN。除这些之外的其他所有值都被视为 "Truthy" 值,即在布尔上下文中会被当作真值处理。当我们使用 napi_get_value_bool 函数时,JavaScript 中的值会被转换为它对应的布尔值。

现在,让我们通过一个简单的例子来理解这个函数的工作原理。

假设你有如下的 JavaScript 代码:

// 在 JavaScript 中定义了两个变量
let jsTruthyValue = 1; // 这是一个 Truthy 值
let jsFalsyValue = 0; // 这是一个 Falsy 值

如果你正在编写一个 Node.js 的扩展模块,并希望在你的 C++ 代码中获取这些 JavaScript 变量的布尔值表示形式,你可以使用 napi_get_value_bool 函数来实现。

以下是一个如何在 C++ 扩展模块中使用 napi_get_value_bool 函数的大致示例:

##include `<`node_api.h>

// 假设 args[0] 是传递给我们扩展函数的第一个参数
napi_status status;
bool cBoolValue;

// 获取 JavaScript 中传入的第一个参数的布尔值
status = napi_get_value_bool(env, args[0], &cBoolValue);

if (status != napi_ok) {
    // 处理错误...
}

// 现在 cBoolValue 就包含了传入的 JavaScript 值的布尔值表示
printf("The boolean value is: %s\n", cBoolValue ? "true" : "false");

在这个例子中,你可以看到 napi_get_value_bool 是如何工作的。它接收三个参数:

  • env:当前的 N-API 环境句柄。
  • args[0]:你想转换成布尔值的 JavaScript 值。
  • &cBoolValue:一个指向原生 bool 变量的指针,函数执行成功后,这个变量就会包含转换后的布尔值。

一旦调用 napi_get_value_bool 成功执行,原生的 bool 变量 cBoolValue 就会被设置为 truefalse,取决于传入的 JavaScript 值是 Truthy 还是 Falsy。

在你的 Node.js 项目中,如果你需要在原生 C/C++ 代码里正确处理 JavaScript 环境传入的布尔值,这个功能就非常重要。通过这种方式,你可以确保 JavaScript 层面上的逻辑在 C/C++ 层面上得到正确的反映,进而在扩展模块中使用这些布尔值进行逻辑控制或配置。

napi_get_value_doubleopen in new window

napi_get_value_double 是 Node.js 中的 N-API 函数,N-API 是一套用于构建本地插件的 C API。Node.js 允许你使用 C, C++ 等语言编写扩展模块,这些扩展模块可以直接操作底层资源或执行高性能计算任务。

napi_get_value_double 的目的是在一个本地插件(C/C++代码)中从 JavaScript 传来的 Number 类型的值中提取出双精度浮点数(即 C/C++ 中的 double 类型)。当你的插件从 JavaScript 接收一个数字,并希望将其用作 C/C++ 中的 double 类型时需要用到它。

函数原型如下:

napi_status napi_get_value_double(napi_env env, napi_value value, double* result);

参数解释:

  • env: 表示当前的环境,通常是每个函数都会有的上下文参数。
  • value: 是一个 napi_value 类型,它代表了 JavaScript 中的一个 Number 值。
  • result: 是指向 double 变量的指针,用于存储转换后的双精度浮点数。

返回值:

  • 返回 napi_status,表示函数调用成功与否的状态码。如果函数执行成功,它通常会返回 napi_ok

下面举一个实际的例子:

假设你正在编写一个本地插件,该插件需要从 JavaScript 接收一个数字,然后使用这个数字进行一些计算。

首先,在你的 C/C++ 源文件中,你可能会有类似下面的代码:

##include `<`node_api.h>

// 这个是你的函数实现,它将被 JavaScript 调用
napi_value CalculateSomething(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    double number;

    // 获取JavaScript传递的参数
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 从args[0]中提取double值
    if (napi_get_value_double(env, args[0], &number) != napi_ok) {
        // 处理错误...
    }

    // 接下来你可以使用 number 进行计算
    double result = number * 2.0; // 假设我们只是简单地将其乘以2

    // 最后你需要将计算结果返回给 JavaScript
    napi_value returnValue;
    napi_create_double(env, result, &returnValue);

    return returnValue;
}

// 初始化函数,将上面的函数暴露给 JavaScript
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, CalculateSomething, NULL, &fn);
    napi_set_named_property(env, exports, "calculate", fn);
    return exports;
}

在 JavaScript 端,你可以像这样调用这个插件的函数:

const nativeAddon = require("./build/Release/native-addon.node");

console.log(nativeAddon.calculate(3.14)); // 输出: 6.28

在这个示例中,JavaScript 代码调用了本地插件的 calculate 函数,并传入了一个数字 3.14。C/C++ 插件接收这个数字,通过 napi_get_value_double 提取为 double 类型,然后将其乘以 2 后返回结果 6.28 给 JavaScript。

这种机制允许 Node.js 扩展其功能并处理高性能计算或更直接地与系统资源交互,同时保持 JavaScript 代码的清晰和简洁。

napi_get_value_bigint_int64open in new window

好的,让我们来简单深入地了解一下 Node.js 中的napi_get_value_bigint_int64函数。

首先,N-API 是 Node.js 的一个 C 语言 API,它用于构建本地插件。本地插件是直接与 Node.js 运行时进行交互的动态链接共享对象,通常用于性能敏感的操作或是对已有 C/C++库的封装。

现在,让我们聚焦在napi_get_value_bigint_int64这个具体的 N-API 函数上。这个函数允许你从 JavaScript 层面传递的一个 BigInt 值转换为 C 语言层面的 int64_t 值。BigInt 是一个 JavaScript 的数据类型,用于表示任意精度的整数。而在 C 语言中,int64_t 则是一个固定大小(64 位)的整型数据类型。

这是napi_get_value_bigint_int64函数的基本声明:

napi_status napi_get_value_bigint_int64(napi_env env,
                                        napi_value value,
                                        int64_t* result,
                                        bool* lossless);
  • env: 这是一个代表 Node.js 环境的句柄,所有的 N-API 调用都需要这个参数。
  • value: 这是一个代表 JavaScript 中 BigInt 值的napi_value
  • result: 这是一个指向int64_t变量的指针,在函数执行后,该变量将存储转换得到的 64 位整数值。
  • lossless: 这是一个指向布尔值的指针,它会告诉你转换是否“无损”,即 BigInt 是否可以完全不丢失任何信息地转换为 64 位整数。

如果 BigInt 值太大,无法以无损方式放入 64 位整数,则lossless指针所指向的值会被设置为false,并且结果可能是截断后的值。

以下是如何在实际的 C 代码中使用该函数的例子:

##include `<`node_api.h>

// 假设我们已经获得了代表BigInt的napi_value, named js_bigint
napi_value js_bigint;

// ... js_bigint 被赋值的代码 ...

int64_t my_number;
bool is_lossless;

// 调用napi_get_value_bigint_int64来转换BigInt到int64_t
napi_status status = napi_get_value_bigint_int64(env, js_bigint, &my_number, &is_lossless);

if (status == napi_ok) {
    if (is_lossless) {
        // 转换成功,并且是无损的
    } else {
        // 转换成功,但是有损的,BigInt值太大,不能完全放入int64_t
    }
} else {
    // 调用失败,处理错误
}

在上述代码中,我们首先定义了一个int64_t类型的变量my_number,以及一个布尔变量is_lossless。然后,我们调用了napi_get_value_bigint_int64函数,期望将一个 BigInt 值转换为一个int64_t值,并通过is_lossless来检测转换是否无损。

请注意,真实世界的情况可能更复杂,因为还涉及到从 JavaScript 传递 BigInt 值到 C 插件等过程,但上述代码展示了如何使用这个特定的 N-API 函数进行基本转换。

napi_get_value_bigint_uint64open in new window

Node.js 中的 napi_get_value_bigint_uint64 是 N-API 的一部分。N-API 是一个 C 语言的 API,允许原生插件(通常用 C 或 C++编写)与 JavaScript 代码进行交互,而不受 Node.js 版本变化的影响。

在 JavaScript 中,BigInt是一种数值类型,可以表示大于Number.MAX_SAFE_INTEGER的整数。相反,在 C/C++等语言中,通常使用特定的数据类型来表示大整数,例如uint64_t(一个无符号的 64 位整数)。

函数napi_get_value_bigint_uint64允许你从一个 JavaScript BigInt对象获取其值,并将这个值作为一个 64 位无符号整数(uint64_t)存放到原生代码变量中。

以下是napi_get_value_bigint_uint64函数的基本用途:

  1. 从 JavaScript 传递一个大整数到原生插件。
  2. 在原生插件中,使用napi_get_value_bigint_uint64提取这个大整数的值。
  3. 使用这个值进行一些 C/C++层面的操作,比如算术操作、二进制操作等。

现实应用示例:

  1. 文件系统访问:想象一个 Node.js 应用,需要处理文件的大小。有时候,文件可能非常大,超过了标准 JavaScript 数值能够安全表示的范围。这时,你可能会用到BigInt。如果你正在编写一个扩展模块来优化文件大小的检索,那么你可能需要用到napi_get_value_bigint_uint64来正确地处理这些大的数值。

  2. 高精度时间戳:某些应用程序可能需要高精度时间戳,这些时间戳的精度超出了 JavaScript 普通数字类型的范围。例如,当你要与一些需要高精度计时的硬件设备交互时,可能会用到BigInt来表示这些时间戳。在原生模块中,你将使用napi_get_value_bigint_uint64来接收和处理这些高精度的值。

  3. 大整数运算:假如你的应用需要执行大整数加密算法,如 RSA 加密,这通常涉及到很大的数值运算。在这种情况下,JavaScript 的BigInt可用于表示这些大数,而napi_get_value_bigint_uint64则可用于将它们传递给执行加密算法的原生插件代码。

举一个简单的代码例子:

##include `<`node_api.h>

// 假设这个函数被调用时,info[0] 是一个 BigInt 对象。
napi_value GetBigIntAsUint64(napi_env env, napi_callback_info info) {
  napi_status status;
  size_t argc = 1;
  napi_value args[1];
  uint64_t result;

  // 获取 JavaScript 函数中的参数列表
  status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 将 BigInt 转换为 uint64_t
  bool lossless; // 这个变量表示转换是否损失精度
  status = napi_get_value_bigint_uint64(env, args[0], &result, &lossless);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 现在 result 变量中包含了 BigInt 的值,并可以在 C/C++ 代码中使用它

  // ... 你的代码逻辑 ...

  // 返回一个新的 BigInt 给 JavaScript
  napi_value new_bigint;
  status = napi_create_bigint_uint64(env, result, &new_bigint);
  if (status != napi_ok) {
    // 处理错误...
  }

  return new_bigint;
}

在上述例子中,我们定义了一个函数GetBigIntAsUint64,它从 JavaScript 接收一个BigInt对象,然后使用napi_get_value_bigint_uint64将其转换为uint64_t类型,并可以在 C/C++中对它进行操作。最后,我们创建了一个新的BigInt对象,将其返回给 JavaScript。

请注意,为了确保类型安全和数据精度,你需要检查lossless变量。如果losslessfalse,说明转换过程中损失了精度,这通常发生在BigInt值超出了uint64_t能够表示的范围的情况。

napi_get_value_bigint_wordsopen in new window

napi_get_value_bigint_words 是 Node.js 中 N-API(原生 API)的一部分,它让你可以从 JavaScript 的 BigInt 类型中提取数据并在 C 或者 C++ 原生模块中使用。BigInt 是 JavaScript 中一种可以表示任意大整数的数据类型。

在 Node.js v21.7.1 的文档中,napi_get_value_bigint_words 函数允许你将一个 JavaScript 的 BigInt 对象转换成由多个数字组成的数组(通常是 64 位长整数),这样你就可以在原生代码中处理非常大的整数。

下面我会详细解释这个函数是如何工作的,并给出一个实际应用的例子。

函数声明

这个函数在 C 或 C++代码中的声明类似于:

napi_status napi_get_value_bigint_words(
    napi_env env,
    napi_value value,
    int* sign_bit,
    size_t* word_count,
    uint64_t* words);
  • env: 当前的 N-API 环境,它代表了 Node.js 运行时的上下文。
  • value: 一个 napi_value 类型的变量,它包装了一个 JavaScript 的 BigInt 对象。
  • sign_bit: 指向一个 int 的指针,函数会设置该值为 0 或 1 来表示 BigInt 的正负。
  • word_count: 一个指向 size_t 的指针,在调用函数之前,你需要设置它为 words 数组可以容纳的元素数量。调用后,它会被设置为实际需要的元素数量。
  • words: 指向一个 uint64_t 数组的指针,这个数组用来存储大整数的二进制表示。

示例

假设你正在编写一个原生模块,需要操作一个非常大的整数。下面是如何在你的 C/C++ 代码中调用 napi_get_value_bigint_words 的一个简单例子。

##include `<`node_api.h>

// 假设 'bigIntValue' 是一个包含了 BigInt 的 napi_value
napi_value bigIntValue;

// 在其他地方获取到这个 bigIntValue ...

// 准备用于提取 BigInt 信息的变量
int signBit;
size_t wordCount = 2; // 假设我们知道 BigInt 不会超过 128 位
uint64_t words[2]; // 用于存储 BigInt 二进制表示的数组

// 调用函数提取 BigInt
napi_status status = napi_get_value_bigint_words(env, bigIntValue, &signBit, &wordCount, words);

// 检查函数调用是否成功
if (status != napi_ok) {
  // 处理错误...
}

// 'signBit' 现在代表了 BigInt 的符号 (0 为正, 1 为负)
// 'wordCount' 已经被设置为实际需要的元素数量
// 'words' 数组现在包含了 BigInt 的二进制表示

使用 napi_get_value_bigint_words 函数,你可以在 C/C++ 代码中获取到 JavaScript BigInt 的精确表示,然后进行各种计算,比如加密算法、大数运算等对大整数有特殊需求的场合。

总结起来,napi_get_value_bigint_words 是 Node.js 原生编程接口中与大整数互操作的重要工具,它让原生模块能够处理那些超出标准 JavaScript 数字范围的整数。

napi_get_value_externalopen in new window

Node.js 中的 N-API(Node.js API)是一个 C 语言级别的 API,它允许本地插件(通常用 C 或 C++编写)与 JavaScript 代码进行互操作。napi_get_value_external是 N-API 提供的一个函数,它被用于从一个 JavaScript 值中检索一个在 C 环境下创建的外部数据指针。

当你在 JavaScript 和 C/C++混合编程时,并且需要在两者之间传递数据,尤其是不能直接在 JavaScript 中创建的非 JS 类型数据时,这个功能就显得特别重要。napi_get_value_external函数允许你在 JavaScript 层面上持有并操作 C/C++层面的数据对象。

下面通过几个步骤来解释它的工作原理:

  1. 在 C/C++层面创建数据:首先,在 C/C++插件中创建一个数据对象。这可能是任何类型的数据,比如一个自定义的结构体或者类的实例。

  2. 将数据包装为napi_value:使用napi_create_external等函数将这个 C/C++层面的数据转换成一个可以在 JavaScript 中使用的napi_value。这个过程称为“封装”(wrapping)。

  3. 在 JavaScript 中使用这个值:一旦数据被封装为napi_value,它就可以作为一个 JS 变量传递到 JavaScript 代码中,并在那里被当做一个“外部”类型的值使用。

  4. 从 JavaScript 值中检索数据:当你想要在 C/C++代码中再次获取原始数据时,你可以使用napi_get_value_external函数。这个函数将会从 JS 层面表示外部数据的napi_value中提取出 C/C++层面的指针。

现在,让我们以具体的示例说明这一过程:

示例 - C/C++侧

假设你有一个 C++类,如下所示:

class MyData {
public:
    int value;
    // 构造函数
    MyData(int val) : value(val) {}
};

然后你创建了一个对象,并将它封装为napi_value

MyData* myData = new MyData(42);
napi_value externalValue;
// 假设 `env` 是当前的napi环境,`myData` 是我们要封装的指针
napi_create_external(env, (void*)myData, nullptr, nullptr, &externalValue);

示例 - JavaScript 侧

现在,在 JavaScript 代码中,你可以获得这个externalValue,它代表了一个外部数据:

// 这个externalValue是通过某种机制从C++传递到JS的
console.log(externalValue); // 这将不会显示实际数据,因为它是外部的

示例 - 重新获取 C/C++数据

最后,如果你希望在 C/C++插件中再次访问这个数据,你可以使用napi_get_value_external

void* data;
// 再次假设 `env` 是当前的napi环境
napi_get_value_external(env, externalValue, &data);
MyData* originalData = static_cast`<`MyData*>(data); // 现在你又得到了原始的C++对象指针
std::cout `<``<` "The value is: " `<``<` originalData->value `<``<` std::endl; // 将输出:The value is: 42

通过这个过程,你可以在 JavaScript 代码和 C/C++代码之间安全地传递复杂的数据结构,并在需要时对其进行操作。这使得 Node.js 能够通过本地扩展来执行高性能或低级操作,同时保持 JS 层面上的简介和易用性。

napi_get_value_int32open in new window

在 Node.js 中,N-API 是一个用于构建原生插件的 API 层。原生插件是用 C 或 C++编写的模块,可以直接调用 Node.js 内部和 V8(JavaScript 引擎)的功能。N-API 的目的是减少原生插件与 Node.js 版本之间的兼容性问题,并简化原生模块的编写。

napi_get_value_int32函数就是 N-API 提供的一种功能,其作用是从一个 N-API 的napi_value类型中提取出一个 32 位整数(即 C 语言中的int32_t类型)。napi_value是表示 JavaScript 值的 N-API 结构体,在本地代码中不能直接操作 JavaScript 值,所以需要通过这类函数来进行转换。

使用场景

假设你正在编写一个 Node.js 原生插件,你想要创建一个函数,它接受一个 JavaScript 数字并将其翻倍。在这个过程中,你需要从 JavaScript 传入的参数中获取实际的数字值,并在 C/C++代码中使用这个值。

函数原型

napi_status napi_get_value_int32(napi_env env,
                                 napi_value value,
                                 int32_t* result);
  • napi_env env: 当前环境的句柄,代表了当前的 Node.js 环境。
  • napi_value value: 你想要转换的 JavaScript 值。
  • int32_t* result: 这是结果参数的指针,在函数执行后,整数值会被存储在这里。

返回值

该函数返回napi_status枚举值,告诉我们函数调用是否成功。如果返回值不是napi_ok,则意味着有错误发生。

实例代码

下面是一个简单的例子,展示了如何在一个 Native 插件的函数中使用napi_get_value_int32

##include `<`node_api.h>

// 原生函数,将输入的JavaScript整数翻倍
napi_value DoubleInt(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 1;
    napi_value args[1];
    int32_t number;

    // 获取JavaScript传递的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (status != napi_ok) {
        // 错误处理
    }

    // 将第一个参数转换为int32
    status = napi_get_value_int32(env, args[0], &number);
    if (status != napi_ok) {
        // 错误处理
    }

    // 计算翻倍后的值
    int32_t doubledNumber = number * 2;

    // 返回结果给JavaScript
    napi_value result;
    status = napi_create_int32(env, doubledNumber, &result);
    if (status != napi_ok) {
        // 错误处理
    }

    return result;
}

// 初始化函数,注册DoubleInt到exports对象
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    // 创建一个新的函数
    status = napi_create_function(env, NULL, 0, DoubleInt, NULL, &fn);
    if (status != napi_ok) {
        // 错误处理
    }

    // 设置导出的属性
    status = napi_set_named_property(env, exports, "doubleInt", fn);
    if (status != napi_ok) {
        // 错误处理
    }

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中,我们创建了一个名为DoubleInt的函数,它接收一个 JavaScript 传的整数参数,将其翻倍,并返回结果。napi_get_value_int32就是用于获取传入参数并将其转换为一个本地的int32_t类型的整数值。

开发者需要明白的是,当使用 N-API 编写原生插件时,他们通常需要处理类型转换,因为 JavaScript 中的数据类型和 C/C++中的数据类型是不同的。而napi_get_value_int32正是帮助我们进行这种类型转换的工具之一。

napi_get_value_int64open in new window

当然,我会尽力解释得通俗易懂。

Node.js 中的 napi_get_value_int64

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时。它允许你在服务器端运行 JavaScript 代码。Node.js 提供了一套丰富的 API 来与操作系统交互,这些 API 集合称为 N-API。N-API 是 Node.js 的一个功能,允许你用 C 或 C++ 编写扩展模块。这些扩展模块可以直接调用 Node.js 提供的各种底层 API,以此来创建高性能的应用程序。

napi_get_value_int64 是什么?

napi_get_value_int64 是 Node.js 提供的 N-API 中的一个函数,它允许你将一个 JavaScript 数值(Number)转换成一个 C 语言中的 int64_t 类型。int64_t 是一个表示 64 位整数的数据类型,在 C/C++ 中就相当于长整型(long integer),它能够存储非常大或非常小的整数值。

napi_get_value_int64 使用场景:

假设你正在编写一个 Node.js 扩展,需要处理来自 JavaScript 端的大整数,并且要在 C/C++ 插件中进行一些复杂的计算或者系统调用,那么你可能就需要使用 napi_get_value_int64 函数。

示例代码:

假如我们有一个 JavaScript 中的数字,我们要在扩展模块中处理它。下面是一个简化的例子:

JavaScript 端调用扩展模块的示例代码:

const myExtension = require("my-native-extension");

const bigNumber = 1234567890123456789n; // 假设这是一个大整数
myExtension.processLargeNumber(bigNumber);

C/C++ 扩展模块中的相关部分:

##include `<`node_api.h>

// 这个函数就是被 JavaScript 调用的
napi_value ProcessLargeNumber(napi_env env, napi_callback_info info) {
    napi_status status;

    size_t argc = 1;
    napi_value args[1];

    // 获取 JavaScript 调用传递的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (status != napi_ok) {
        // 处理错误...
    }

    int64_t number;

    // 将 JavaScript 的 Number 转换为 C 的 int64_t
    status = napi_get_value_int64(env, args[0], &number);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 现在我们有了一个 C 语言中的 int64_t 类型的整数,可以进行处理了
    // ... 做一些处理,例如:
    printf("Received number from JS: %lld\n", number);

    // 返回处理结果给 JavaScript
    // ...
}

// 初始化和注册 ProcessLargeNumber 函数
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, ProcessLargeNumber, NULL, &fn);
    napi_set_named_property(env, exports, "processLargeNumber", fn);
    return exports;
}

在上面的示例中,我们创建了一个名为 ProcessLargeNumber 的函数,这个函数从 JavaScript 端接收一个参数,并使用 napi_get_value_int64 转换该参数为 int64_t 类型的变量。然后,我们可以在 C/C++ 代码中处理这个大整数。

请记住,这只是一个非常简化的例子,实际编写 Node.js 的 C/C++ 扩展涉及很多其他的注意事项和步骤。不过,希望这个例子能够帮助你理解 napi_get_value_int64 在 Node.js 扩展中的应用。

napi_get_value_string_latin1open in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。它允许你在服务器端运行 JavaScript 代码。Node.js 提供了一组核心的库,用于处理文件、网络通信等功能。

N-API(Node.js API)是 Node.js 提供的一个稳定且独立于 JavaScript 运行时的 C API 层,它用于构建原生插件。原生插件是用 C 或 C++ 写的模块,它们可以直接与 Node.js 的底层 API 交互,提供比普通 JavaScript 更接近系统底层的性能和功能。

napi_get_value_string_latin1 是 N-API 中的一个函数,它用于将 JavaScript 字符串转换成一个以 Latin-1 编码(也称为 ISO-8859-1)的 C 字符串。Latin-1 编码是一种单字节编码方案,能够表示 Western European(西欧)语言中的字符。

当你有一个 JavaScript 字符串,并且需要在原生插件中使用这个字符串的时候,你可能会使用 napi_get_value_string_latin1 函数。这个函数会把 JavaScript 字符串内的字符按照 Latin-1 编码复制到一个由你分配的缓冲区(buffer)中。

举例来说:

假设你在 Node.js 中创建了一个原生插件,它需要从 JavaScript 接收一个字符串参数并以某种方式处理这个字符串。在 C/C++ 的原生插件代码中,你可能会这样使用 napi_get_value_string_latin1

##include `<`node_api.h>

// ... 其他必要的头文件和代码 ...

// 这是一个被暴露给 JavaScript 的函数,它接受一个字符串参数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1];
    napi_value thisArg;
    void* data;

    // 获取函数参数
    napi_get_cb_info(env, info, &argc, argv, &thisArg, &data);

    // 确保我们得到了一个字符串
    napi_valuetype valuetype;
    napi_typeof(env, argv[0], &valuetype);
    if (valuetype != napi_string) {
        napi_throw_type_error(env, NULL, "Argument must be a string.");
        return NULL;
    }

    // 预先获取字符串长度
    size_t str_length;
    napi_get_value_string_latin1(env, argv[0], NULL, 0, &str_length);

    // 分配足够大小的缓冲区来存储 Latin-1 字符串
    char* c_str = (char*) malloc((str_length + 1) * sizeof(char));

    // 将 JavaScript 字符串复制到缓冲区
    napi_get_value_string_latin1(env, argv[0], c_str, str_length + 1, &str_length);

    // c_str 包含了转换后的字符串,可以进行处理
    // ...

    // 处理完成后记得释放分配的内存
    free(c_str);

    // 返回结果或者 undefined...
    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

// ... 注册 MyFunction 使其能够在 JavaScript 中调用 ...

上面的例子中,MyFunction 是一个原生函数,它接受一个字符串参数。在函数内部,它先检查确保传入的参数确实是一个字符串,然后计算这个字符串的长度,之后为这个字符串分配足够的内存,并使用 napi_get_value_string_latin1 函数将 JavaScript 字符串的值复制到分配的内存中。这样我们就可以在原生代码中操作这个字符串了。

请注意,因为 Latin-1 只能表示范围有限的字符,如果你的 JavaScript 字符串包含不能用 Latin-1 编码表示的字符,那么这些字符将被替换为一个特殊的替换字符,通常是 ?。因此,在处理多种字符集或者需要 Unicode 支持的应用程序中,你可能需要使用其他函数来获取 UTF-8 编码的字符串。

napi_get_value_string_utf8open in new window

好的,Node.js 中 napi_get_value_string_utf8 是一个函数,属于 Node.js 的 N-API(原生 API)库。N-API 提供了一组用 C 语言写的接口,允许你创建可以直接与 JavaScript 交互的本地插件(native addons)。

在解释 napi_get_value_string_utf8 之前,有必要了解以下几个概念:

  1. Native Addons: 这是指使用 C 或 C++等语言编写的模块,它们可以被 Node.js 加载,并提供比 JavaScript 更高性能的操作。
  2. N-API: 是一种稳定的 Node.js API 层,使得原生模块(native addons)隔离于底层的 V8 引擎,这样即使 V8 有更新,原生模块也不需要重新编译。
  3. JavaScript Values and N-API: 在使用 N-API 工作时,你通常需要处理从 JavaScript 传递到 C 层的值,例如字符串、数字等。

现在让我们来看看 napi_get_value_string_utf8 函数:

napi_get_value_string_utf8

功能:这个函数的作用是将一个 N-API 类型的字符串(napi_value),转换成 UTF-8 编码的 C 字符串。

参数

  • env: 代表 N-API 环境的句柄,它是每次调用 N-API 函数时必须提供的。
  • value: 要读取的 N-API 字符串类型的值。
  • buf: 指向预先分配的缓冲区的指针,用来存放 UTF-8 编码的字符串。
  • bufsize: 缓冲区的大小(字节为单位)。
  • result: 实际复制到缓冲区的字节数。

返回值:如果函数成功执行,则返回 napi_ok,否则返回错误码。

例子

假设你在 JavaScript 中有一个字符串,你想在 C 层处理这个字符串。首先你需要创建一个原生模块,然后在这个模块里使用 napi_get_value_string_utf8 来获取 UTF-8 编码的 C 字符串。

##include `<`node_api.h>

// 假设你已经定义了一个函数要从JS中获取字符串
napi_value GetStringFromJS(napi_env env, napi_callback_info info) {
    napi_status status;

    // 获取参数数量和参数数组
    size_t argc = 1;
    napi_value argv[1];
    status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    // 确保获取状态正确并且至少有一个参数
    if (status != napi_ok || argc `<` 1) {
        // 错误处理...
    }

    // 获取字符串长度
    size_t str_length;
    status = napi_get_value_string_utf8(env, argv[0], NULL, 0, &str_length);

    if (status != napi_ok) {
        // 错误处理...
    }

    // 分配内存来存储字符串
    char* buf = (char*)malloc(str_length + 1); // 加1为了null-terminator

    size_t copied_length;
    status = napi_get_value_string_utf8(env, argv[0], buf, str_length + 1, &copied_length);

    if (status != napi_ok) {
        free(buf);
        // 错误处理...
    }

    // 此时buf中有了从JS传递过来的字符串内容
    // 可以继续进行其他操作...

    // 最后别忘记释放分配的内存
    free(buf);

    // 返回一个undefined类型的值到JS
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

在此例中,首先确定了传入的参数数量和类型,然后通过 napi_get_value_string_utf8 函数获取了一个 JavaScript 字符串的 UTF-8 表示形式。需要注意的是,为了获得字符串实际字符数,首先调用了一次该函数并传递 NULL 作为缓冲区,这样就可以得到字符串长度,然后再进行实际的内存分配。

这个函数主要用于当你编写原生模块需要处理 JavaScript 层传递的字符串数据时。通过转换成 C 字符串,你就可以应用各种 C 语言的操作来处理这些数据。

napi_get_value_string_utf16open in new window

Node.js 中的 napi_get_value_string_utf16 是一个函数,属于 Node.js 的 N-API(原生 API)。N-API 提供了一组底层的 API,使得 C/C++的插件模块编写者可以不用关心具体的 V8 或 Chakra 引擎的版本差异,编写出兼容性更强、稳定性更高的原生模块。

napi_get_value_string_utf16 函数的作用是将一个 JavaScript 字符串转换为 UTF-16 编码的字符数组。UTF-16 是一种字符编码方式,它可以用来表示世界上大部分的书面语言。

在 Node.js 的原生模块中使用此函数通常需要完成以下步骤:

  1. 接收一个从 JavaScript 传递过来的字符串参数。
  2. 使用 napi_get_value_string_utf16 函数将这个字符串参数转换成 C/C++ 中的 UTF-16 编码的字符数组。
  3. 在 C/C++ 代码中处理这个字符数组。
  4. 可能还需要将处理后的结果返回给 JavaScript。

下面我会通过一个具体的例子来解释如何使用 napi_get_value_string_utf16

##include `<`node_api.h>

// 假设这个函数是从 JavaScript 调用的原生函数
napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_status status;

    // 获取从 JavaScript 传递过来的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查是否有一个参数,并且参数是字符串
    if (status == napi_ok && argc == 1) {
        napi_valuetype valuetype;
        napi_typeof(env, args[0], &valuetype);

        if (valuetype == napi_string) {
            // 首先获取字符串的长度
            size_t str_length;
            status = napi_get_value_string_utf16(env, args[0], NULL, 0, &str_length);

            if (status == napi_ok) {
                // 分配足够的空间保存 UTF-16 字符串
                u_char16_t* buf = new u_char16_t[str_length + 1];

                // 将 JavaScript 字符串转换为 UTF-16 编码的字符数组
                size_t copied;
                status = napi_get_value_string_utf16(env, args[0], buf, str_length + 1, &copied);

                if (status == napi_ok) {
                    // 这里可以对 buf 中的 UTF-16 编码的字符数组进行处理
                    // ...

                    // 处理完毕后要释放之前分配的内存
                    delete[] buf;
                }
            }
        }
    }

    // 返回 undefined
    napi_value undef;
    napi_get_undefined(env, &undef);
    return undef;
}

void Initialize(napi_env env, napi_value exports) {
    napi_value my_function;
    // ... 创建和导出 MyNativeFunction 函数 ...
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Initialize)

在上面的例子中,MyNativeFunction 是一个原生函数,它会被 JavaScript 代码调用。这个函数首先确定被调用时传入了一个参数,并且这个参数是字符串。然后,它使用 napi_get_value_string_utf16 函数来获取字符串的长度,并分配一个足够大的缓冲区来存储 UTF-16 编码的字符串。接着,再次调用 napi_get_value_string_utf16 函数实际读取字符串并将其拷贝到之前分配的缓冲区中。这样我们就可以在 C/C++ 中处理这个字符串了。处理完成后,记得释放分配的内存以避免内存泄露。

这个函数在开发 Node.js 原生扩展时非常有用,尤其是当你需要处理与文本相关的操作时。通过这种方式,你可以在 C/C++ 层面上操作 JavaScript 字符串,以高效地执行各种任务,比如字符串分析、转换或其他计算密集型任务。

napi_get_value_uint32open in new window

当然,让我为你解释一下 Node.js 中的napi_get_value_uint32函数。

在 Node.js 中,N-API 是一个用来构建本地插件的 API 层。它允许你使用 C 或 C++代码与 Node.js 进行交互,这意味着你可以编写能够以机器代码速度运行的 JavaScript 扩展,这对于性能敏感的应用程序来说非常重要。

现在,我们谈到了napi_get_value_uint32,这个函数是 N-API 的一部分,它被用来将一个 JavaScript 数值转换成 C 语言中的无符号 32 位整数(即uint32_t类型的值)。

使用napi_get_value_uint32时,需要传入两个参数:

  1. napi_env env: 这是 N-API 环境的句柄,它代表了 Node.js 运行时的上下文和状态。每次 N-API 函数调用都需要这个参数。
  2. napi_value value: 这是一个 N-API 值的句柄,它表示一个 JavaScript 值。在这种情况下,我们期望这个 JavaScript 值是一个数值,因为我们想把它转换成一个uint32_t整数。

函数还需要一个地方来存放转换后的uint32_t值。我们通常会提供一个指向uint32_t变量的指针作为输出参数。

下面是一个简单的例子说明如何使用napi_get_value_uint32

假设你有一个 Node.js 的本地插件函数,你想从 JavaScript 传递一个数字给这个函数,并且在 C 层将其读取为一个uint32_t类型的值。

##include `<`node_api.h>

// 假设这是你的本地插件函数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    uint32_t number;
    napi_status status;

    // 获取JavaScript传入的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 将第一个参数(应该是一个JavaScript数值)转换为uint32_t
    status = napi_get_value_uint32(env, args[0], &number);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 现在可以使用number变量,它包含了从JavaScript传过来的数值
    // 执行一些操作...

    // 返回undefined给JavaScript
    napi_value result;
    status = napi_get_undefined(env, &result);
    return result;
}

// 注册上面的函数到Node.js
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

在这个例子中,MyFunction是一个将要暴露给 JavaScript 的本地插件函数。它尝试读取传递给它的第一个参数,并将其转换为一个无符号 32 位整数。如果转换成功,你就可以在 C/C++代码中使用这个值了。最后,如果没有错误发生,函数会返回 undefined 给 JavaScript(这是 Node.js 中函数不返回任何值的标准方式)。

请记住,这里只是一个简单的例子,实际情况可能更加复杂,而且会涉及到更多的错误检查和处理。希望这有助于你理解napi_get_value_uint32的作用和如何在实践中使用它。

Functions to get global instancesopen in new window

Node.js 的 N-API 是一个用于构建原生插件的 API 层,它提供了一种在不同版本的 Node.js 之间保持兼容性的方式。N-API 是原生模块和 Node.js 运行时之间的桥梁,允许开发者使用 C 或 C++ 编写可以直接与 JavaScript 交互的代码。

在 Node.js v21.7.1 的文档中,"Functions to get global instances" 指的是一组特定的函数,这些函数允许你获取全局对象的引用,比如 global 对象本身或者某些像 ArrayBuffer 这样的全局构造函数。全局对象是在所有模块中始终可用的顶级对象。

这些函数通常在编写需要与全局对象直接交互的原生模块时使用。下面我会解释一些这样的函数,并提供一些实际应用的例子:

napi_get_global

这个函数用来获取对 JavaScript 全局对象的引用。在 Node.js 中,全局对象包含了像 console, process 等全局可用的变量和函数。

napi_value global;
napi_status status = napi_get_global(env, &global);
// 接下来可以使用global变量来调用全局方法或访问全局属性。

napi_get_constructor

这个函数可以用来获取 JavaScript 中某个内置对象(比如ArrayObject)的构造函数的引用。

napi_value constructor;
napi_status status = napi_get_constructor(env, object, &constructor);
// object 是一个指向 JavaScript 对象的引用,比如 ArrayBuffer 实例。
// 构造函数可以用来创建新的实例或者调用与构造函数相关的静态方法。

应用例子

假设你正在编写一个原生模块,该模块需要在 JavaScript 环境中创建一个新的 ArrayBuffer 对象。你可以使用 napi_get_global 函数获取全局对象,然后使用 napi_get_named_property 来获得 ArrayBuffer 构造函数的引用,接着就可以通过这个构造函数创建 ArrayBuffer 实例。

以下是 C/C++代码的简化示例,展示了如何使用 N-API 创建一个新的 ArrayBuffer 实例:

##include `<`node_api.h>

napi_value CreateArrayBuffer(napi_env env, napi_callback_info info) {
  napi_value global, arrayBufferConstructor, arrayBufferInstance;
  size_t byteLength = 64; // 假设我们想创建一个64字节长度的ArrayBuffer

  // 获取全局对象
  napi_get_global(env, &global);

  // 获取ArrayBuffer的构造函数
  napi_get_named_property(env, global, "ArrayBuffer", &arrayBufferConstructor);

  // 使用构造函数创建一个新的ArrayBuffer实例
  napi_create_arraybuffer(env, byteLength, NULL, &arrayBufferInstance);

  return arrayBufferInstance;
}

// 之后你需要注册这个函数到你的模块导出中,以便JavaScript可以调用它。

在 Node.js 中使用这个原生模块的 JavaScript 代码可能如下:

const nativeModule = require("your-native-module");
const arrayBuffer = nativeModule.CreateArrayBuffer();
console.log(arrayBuffer.byteLength); // 输出: 64

这只是一个非常基本的例子。在实际开发中,使用 N-API 和原生模块可以执行更复杂的操作,比如性能优化、直接与操作系统 API 交互、处理图形和音频数据等。

napi_get_booleanopen in new window

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎构建的平台,它允许你在服务器端运行 JavaScript。N-API 是 Node.js 提供的一个稳定的 API 层,使得原生模块(用 C 或 C++ 写的扩展)可以不受 Node.js 版本影响的情况下编译和运行。

在 Node.js v21.7.1 的文档中,napi_get_boolean 是 N-API 中的一个函数,它的作用是在原生代码中创建一个新的 JavaScript 布尔值。

这个函数的定义如下:

napi_status napi_get_boolean(napi_env env, bool value, napi_value* result)
  • env:这是一个表示 N-API 环境的句柄,你在调用任何 N-API 函数时都需要提供这个参数。
  • value:这是你想要创建的布尔值,用 C 语言中的 truefalse 表示。
  • result:这是一个指向 napi_value 的指针,napi_get_boolean 函数会将创建好的 JavaScript 布尔值存放在其中。

如果函数执行成功,它会返回 napi_ok,这表示操作成功完成。

下面是如何在一个原生模块中使用 napi_get_boolean 的例子:

##include `<`node_api.h>

// 示例函数,创建一个 JS 布尔值并返回给 JavaScript
napi_value GetTrueValue(napi_env env, napi_callback_info info) {
    napi_value true_value;
    napi_status status;

    // 调用 napi_get_boolean 来创建一个表示 true 的 JavaScript 布尔值
    status = napi_get_boolean(env, true, &true_value);

    // 检查是否调用成功
    if (status != napi_ok) {
        // 处理错误情况
    }

    // 返回创建的布尔值
    return true_value;
}

// 初始化函数,在模块加载时设置导出
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;
    napi_status status;

    // 创建一个新的函数对象
    status = napi_create_function(env, NULL, 0, GetTrueValue, NULL, &fn);

    // 将新创建的函数作为模块的导出
    status = napi_set_named_property(env, exports, "getTrue", fn);

    return exports;
}

// 定义模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的代码中,我们定义了一个名为 GetTrueValue 的函数,该函数使用 napi_get_boolean 创建一个表示 true 的 JavaScript 布尔值,并返回给调用它的 JavaScript 代码。然后我们在 Init 函数中将 GetTrueValue 作为模块的导出,这样 JavaScript 代码就可以通过 require 加载这个模块,并调用 getTrue 函数来获取一个 JavaScript 中的 true 值。

最终,当这个原生模块被加载到 Node.js 应用中时,JavaScript 开发者可以像这样使用它:

const myNativeModule = require("my-native-module");
console.log(myNativeModule.getTrue()); // 输出: true

这里 'my-native-module' 是假设的原生模块名称,在实际应用中应替换为相应模块的真实名称。

napi_get_globalopen in new window

Node.js 中的 N-API 是一个用于创建本地插件(native addons)的 API。本地插件是用 C、C++等编程语言编写的,可以直接调用 Node.js 运行时和 V8 JavaScript 引擎的功能,通常用于性能关键型任务或者调用系统级别的 API。

当你使用 N-API 编写本地插件时,经常需要和 JavaScript 的全局对象进行交互。JavaScript 中的全局对象类似于一个大容器,它包含了 JavaScript 环境中所有的全局变量、函数等。在浏览器中,这个全局对象就是window;而在 Node.js 中,这个全局对象通常指的是global

napi_get_global是 N-API 提供的一个函数,用于获取当前环境的全局对象。你可能会问,为什么要通过 N-API 来获取全局对象呢?因为 N-API 保护了代码不受 V8 引擎版本更新的影响,增加了代码的兼容性和稳定性。

下面是一个如何使用napi_get_global的简单例子:

假设我们想要从 C++代码中创建一个新的 JavaScript 函数,并将其挂载到全局对象上,让 JavaScript 代码可以直接调用这个函数。

首先,在 C++文件中,我们需要包含必要的头文件并定义我们的方法:

##include `<`node_api.h>

// 这个是我们要暴露给JavaScript的函数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    // 在这里实现你的功能
}

// 初始化方法,用来设置导出函数
napi_value Init(napi_env env, napi_value exports) {
    // 首先获取全局对象
    napi_value global;
    napi_status status = napi_get_global(env, &global);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 创建一个新的函数
    napi_value myFunction;
    status = napi_create_function(env, NULL, 0, MyFunction, NULL, &myFunction);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 将函数挂载到全局对象上
    status = napi_set_named_property(env, global, "myGlobalFunction", myFunction);
    if (status != napi_ok) {
        // 处理错误...
    }

    return exports;
}

// 注册模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的代码中,Init函数创建了一个 JavaScript 函数MyFunction,然后通过调用napi_get_global获得全局对象,并通过napi_set_named_propertyMyFunction设置为全局对象的属性。这样一来,当这个本地插件被加载进 Node.js 时,JavaScript 代码就可以直接通过myGlobalFunction()来调用这个函数了。

最后,在 JavaScript 文件中,你可以像这样调用 C++函数:

// 假设你的本地插件叫做'addon'
const addon = require("./build/Release/addon");

// 直接使用在全局对象上挂载的函数
myGlobalFunction();

这只是一个简化示例,真实情况下你可能需要处理更多的边缘情况和错误。但希望通过这个例子,你能理解napi_get_global在 Node.js 中的作用及其基本用法。

napi_get_nullopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API,它提供了一组用 C 语言编写的 API,允许你创建可以直接与 JavaScript 运行时交互的本地代码。这样做的好处是能够在本地代码中执行高性能或更底层操作,同时保持跨不同版本 Node.js 的兼容性。

napi_get_null 是 N-API 中的一个函数,它用于获取 JavaScript 的 null 值的引用。在编写原生插件时,可能需要返回一个 null 值给 JavaScript 环境,napi_get_null 允许你做到这一点。

下面是一个使用 napi_get_null 函数的简单例子:

假设我们正在编写一个原生模块,该模块包含一个函数,当某个条件满足时要返回 null 给 JavaScript:

##include `<`node_api.h>

// 这是我们将暴露给JavaScript的函数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_value result;

    // 检查某个条件是否满足
    bool condition = true; // 假设条件为真

    if (condition) {
        // 如果条件满足,我们想要返回 null 给 JavaScript。
        napi_status status = napi_get_null(env, &result);
        if (status != napi_ok) {
            // 处理错误情况
            napi_throw_error(env, NULL, "Unable to create null value");
            return NULL;
        }
    } else {
        // 如果条件不满足,我们返回其他值,比如数字0
        napi_create_int32(env, 0, &result);
    }

    return result;
}

// 注册上面的函数,使其可以从 JavaScript 代码中调用
NAPI_MODULE_INIT() {
    napi_value fn;

    // 创建一个JavaScript函数
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);

    // 将这个函数设置为导出对象的属性
    napi_set_named_property(env, exports, "myFunction", fn);

    return exports;
}

在这个示例中,如果 condition 为真,则 MyFunction 函数将返回 JavaScript 中的 null; 如果不为真,则会返回数字 0。

要注意的是,以上代码是 C/C++ 代码,它必须与 Node.js 配合使用并通过特定的步骤来编译和绑定。创建完整的原生模块需要一些额外的配置和工具,例如 node-gyp 来编译模块,以及在项目中声明对 N-API 版本的依赖。

希望这个解释和例子帮助您理解 napi_get_null 在 Node.js 中的作用和如何使用!

napi_get_undefinedopen in new window

当然,我很乐意帮助你理解napi_get_undefined这个函数。

N-API 是 Node.js 的一个 API,它允许 C 和 C++代码与 Node.js 交互,也就是说它提供了一系列的工具,使得原生模块(通常用 C 或 C++编写)能够与 JavaScript 代码沟通。napi_get_undefined是其中的一个函数,它的作用是获取 JavaScript 中的undefined值。

在 JavaScript 里,undefined是一个特殊的值,表示变量没有被赋值。在 Native Addon(原生扩展模块)中,经常需要创建 JavaScript 值以便于传递数据或者和 JavaScript 层面的代码进行互动。napi_get_undefined函数就是用来创建这样一个undefined值的。

现在我们来看一个例子。假设你正在编写一个 C++的扩展模块,你可能会需要创建一个函数,在某些条件下返回undefined给 JavaScript。示例代码如下:

##include `<`node_api.h>

// 这个函数将会被暴露给JavaScript,并在被调用时返回undefined
napi_value GetUndefinedExample(napi_env env, napi_callback_info info) {
  // 定义一个用于存放结果的napi_value变量
  napi_value undefined;

  // 调用napi_get_undefined获取JavaScript的undefined值
  napi_status status = napi_get_undefined(env, &undefined);

  // 检查调用是否成功
  if (status != napi_ok) {
    // 如果有错误发生,可以根据实际情况处理错误
    // 这里我们简单地返回空值
    return nullptr;
  }

  // 返回undefined值给JavaScript
  return undefined;
}

// 初始化函数,注册GetUndefinedExample函数到exports对象上
napi_value Init(napi_env env, napi_value exports) {
  napi_status status;
  napi_value fn;

  // 创建一个JavaScript函数
  status = napi_create_function(env, nullptr, 0, GetUndefinedExample, nullptr, &fn);
  if (status != napi_ok) {
    return nullptr;
  }

  // 将这个函数作为"getUndefined"属性添加到exports对象
  status = napi_set_named_property(env, exports, "getUndefined", fn);
  if (status != napi_ok) {
    return nullptr;
  }

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上述的 C++代码中,我们定义了一个GetUndefinedExample函数,这个函数使用了napi_get_undefined来获取一个 undefined 值,并将其返回给调用它的 JavaScript 代码。

在 JavaScript 端,你能够像这样使用这个模块:

const nativeAddon = require("./build/Release/nativeAddon.node");

console.log(nativeAddon.getUndefined()); // 输出:undefined

在这段 JavaScript 代码里,我们首先加载了我们刚才写的原生模块,然后调用了getUndefined函数,并打印了它的返回值,它应该是undefined

Working with JavaScript values and abstract operationsopen in new window

Node.js 是一个能让你使用 JavaScript 来编写服务器端代码的平台。它具备非阻塞 I/O 和事件驱动的特点,这意味着 Node.js 很擅长处理大量并发的数据交互,比如网络应用或实时系统。

N-API(Node API)是 Node.js 提供的一个稳定且版本无关的 API,允许原生插件开发者编译只需一次即可在不同版本的 Node.js 上运行。这就减少了因 Node.js 升级导致的插件兼容性问题。

在 Node.js v21.7.1 的文档中,“Working with JavaScript values and abstract operations”部分描述了如何通过 N-API 在原生代码中创建和操作 JavaScript 值,以及执行一些抽象操作。我们来逐个看一些基本概念和例子:

JavaScript 值 (Values)

JavaScript 中的值可以是数字、字符串、对象、数组等。在原生模块中与这些值进行交互需要使用 N-API 提供的函数。例如,如果你想从 C/C++ 代码中创建一个新的 JavaScript 数字,你可以使用 napi_create_number 函数。

实例:创建一个 JavaScript 数字

##include `<`node_api.h>

napi_value CreateNumber(napi_env env) {
    napi_value num;
    napi_status status = napi_create_double(env, 123.456, &num);
    if (status != napi_ok) {
        // 处理错误...
    }
    return num; // 这是一个 JavaScript 数字
}

抽象操作 (Abstract Operations)

“抽象操作”是指那些定义在 ECMAScript 规范里,对 JavaScript 值进行操作的步骤。在 N-API 中,有很多函数被提供出来以支持这些操作。

实例:调用 JavaScript 函数

如果你有一个 JavaScript 函数,想要从 C/C++ 代码中调用它,你可以使用 napi_call_function

##include `<`node_api.h>

void CallJsFunction(napi_env env, napi_value jsFunction) {
    // 假设我们已经有了一个 'env' 环境标识符和一个 'jsFunction' 表示 JavaScript 函数的值

    // 创建一个 JavaScript 字符串参数
    napi_value arg;
    napi_status status = napi_create_string_utf8(env, "Hello from native code!", -1, &arg);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 调用 JavaScript 函数,传递一个参数
    napi_value result;
    status = napi_call_function(env, global, jsFunction, 1, &arg, &result);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 'result' 现在包含了 JavaScript 函数的返回值
}

在上面的例子中,我们首先创建了一个表示 JavaScript 字符串的 napi_value,然后我们调用 JavaScript 函数,并将创建的字符串作为参数传递给它。

记住,每当你从 C/C++ 代码中操作 JavaScript 对象时,都需要检查返回的 napi_status。这是因为所有的 N-API 函数几乎都会返回状态码,告诉你操作是否成功,或者是否有错误发生。如果有错误发生,你通常需要采取某种措施来处理它,比如抛出一个错误或者停止当前操作。

总结一下,Node.js 中的 N-API 让你能够在原生模块中安全地创建和操作 JavaScript 值,并执行抽象操作。通过这样的方式,你可以构建更高效、跨 Node.js 版本兼容的原生插件。

napi_coerce_to_boolopen in new window

Node.js 中的 napi_coerce_to_bool 是 N-API(原生 API)的一个函数,它用于将一个 JavaScript 值转换成一个布尔值。在 JavaScript 中,很多类型的值都可以有一个“真值”或“假值”的概念,这就是我们通常所说的 truthy 或 falsy 值。

如何理解 bool

在 JavaScript 中,以下值被认为是 falsy,也就是它们会被转换成布尔值 false

  • false
  • 0
  • -0
  • 0n (BigInt 的零)
  • "" (空字符串)
  • null
  • undefined
  • NaN

其他所有值都被认为是 truthy,即转换成布尔值 true。例如:true, 1, "hello", [] (空数组), {} (空对象)等。

napi_coerce_to_bool 的作用

当你在使用 Node.js 的 N-API 编写原生插件时,你可能会处理来自 JavaScript 环境的值。napi_coerce_to_bool 允许你把这些值转换成一个确切的布尔值,这样你就可以在 C 或 C++ 代码中更容易地根据这个布尔值进行逻辑处理。

使用 napi_coerce_to_bool

举个例子,在 Node.js 的原生模块中,你可能接收到了一个 JavaScript 值,并希望检查它是否为 "truthy" 或 "falsy"。你可以使用 napi_coerce_to_bool 来执行这个操作:

##include `<`node_api.h>

// 假设 `env` 是 napi_env 类型的变量,代表N-API环境。
// `value` 是 napi_value 类型的变量,代表JavaScript传递给原生函数的值。

napi_value result;
napi_status status = napi_coerce_to_bool(env, value, &result);

if (status != napi_ok) {
    // 处理错误情况
}

// 现在 `result` 包含了转换后的布尔值

在上面的代码中,value 是一个从 JavaScript 传过来的任意类型的值,napi_coerce_to_bool 函数会尝试将其转化为一个布尔值。转换结果存放在 result 中,这是一个 napi_value 类型的变量,它代表了一个 node.js 中的布尔值。

如果转换成功,你就可以继续使用这个布尔值进行逻辑判断。否则,如果出现了错误(比如传入的不是有效的 JavaScript 值),status 变量会包含表示错误状态的代码,你可以根据这个来进行相应的错误处理。

实际运用示例

如果你正在编写一个需要验证 JavaScript 参数并基于该参数决定行为的原生模块,你可能会像这样使用 napi_coerce_to_bool

napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value result;
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    // 转换第一个参数为布尔值
    napi_coerce_to_bool(env, args[0], &result);

    // 基于布尔值的结果执行不同的操作
    bool c_result;
    napi_get_value_bool(env, result, &c_result);

    if (c_result) {
        // 如果是 true, 执行某些操作
    } else {
        // 如果是 false, 执行其他操作
    }

    // 返回一些结果给 JavaScript 端
    return result;
}

在这个例子中,MyNativeFunction 是一个会被 JavaScript 调用的函数。我们获取了调用这个函数时传递的第一个参数(args[0]),并尝试将其强制转换为布尔值。然后我们根据转换得到的布尔值来决定接下来的行为,并最终返回一个结果给 JavaScript。

napi_coerce_to_numberopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API,它不依赖于 JavaScript 引擎(如 V8)的变化,因此可以保持向后兼容性。napi_coerce_to_number 是其中的一个函数,它的作用是将任何 JavaScript 值转换成数字类型。

在 JavaScript 中,你可以有很多不同类型的值,比如字符串、布尔值、对象等。有时候我们希望把这些值转换成数字进行计算或比较。这就是 napi_coerce_to_number 函数存在的意义。当你创建一个原生插件,并希望接受一个 JavaScript 值并确保它为数字时,这个函数会特别有用。

举几个例子:

  1. 将字符串转换为数字 假设你在 JavaScript 中调用了一个原生插件的函数,传递了一个表示数字的字符串,比如 "123"。在原生插件中,你可以使用 napi_coerce_to_number 来确保你得到一个数字类型的值。

  2. 将布尔值转换为数字 在 JavaScript 中,布尔值 truefalse 可以被隐式地转换为数字 10。如果你传递了一个布尔值到原生插件,你可能需要使用 napi_coerce_to_number 把它转换为数字,然后再进行后续处理。

  3. 处理 uncertain 类型数据 如果你的原生插件功能是做数学运算,但你不确定用户会传递什么类型的数据,你可以使用 napi_coerce_to_number 来尝试将传入的值转换为数字,而不用担心它实际上是不是数字。

代码示例:

假设我们要编写一个简单的原生插件函数 add, 它接收两个参数,把它们都转换为数字,然后相加。

##include `<`node_api.h>

napi_value Add(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 2;
    napi_value args[2];
    double value1, value2;

    // 获取JavaScript传递的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 强制转换第一个参数为数字
    napi_value numberValue1;
    status = napi_coerce_to_number(env, args[0], &numberValue1);
    status = napi_get_value_double(env, numberValue1, &value1);

    // 强制转换第二个参数为数字
    napi_value numberValue2;
    status = napi_coerce_to_number(env, args[1], &numberValue2);
    status = napi_get_value_double(env, numberValue2, &value2);

    // 执行加法运算
    napi_value sum;
    status = napi_create_double(env, value1 + value2, &sum);

    return sum;
}

// 初始化函数,注册 `Add` 函数
NAPI_MODULE_INIT() {
    napi_value add_function;
    napi_create_function(env, NULL, 0, Add, NULL, &add_function);
    napi_set_named_property(env, exports, "add", add_function);
    return exports;
}

在这段代码中,我们首先通过 napi_get_cb_info 获取 JavaScript 传递来的参数,然后分别使用 napi_coerce_to_number 将这两个参数强制转换为数字。随后,我们使用 napi_get_value_double 获取它们的数值,执行加法操作,并最终返回结果。

要注意的是,在实际应用中,我们还需要检查 status 确保每步操作都成功执行,以及处理可能出现的错误,但为了简明起见,这里省略了这些步骤。

napi_coerce_to_objectopen in new window

好的,让我们详细了解一下 Node.js 中的napi_coerce_to_object这个函数。

首先,napi代表的是 Node.js 的原生 API (N-API),它允许你用 C 或 C++编写可加载到 Node.js 环境中的扩展。这样做的优点是可以构建性能更高的模块,并且可以直接使用系统级别的资源或者其他非 JavaScript 资源。

napi_coerce_to_object是 N-API 提供的一个函数,它的作用是将一个 JavaScript 值强制转换成一个 JavaScript 对象。在 JavaScript 中,基本类型(如数字、字符串、布尔值)与对象是有区别的。但有些时候,我们需要将基本类型当作对象来处理。例如,当我们想调用某个基本类型的方法时,就需要将其转为对象。

在 Node.js 的 N-API 中,napi_coerce_to_object函数可以帮助我们做这样的转换。这个函数的签名如下:

napi_status napi_coerce_to_object(napi_env env, napi_value value, napi_value* result);
  • env:表示当前的 N-API 环境。
  • value:是要被转换的 JavaScript 值。
  • result:是一个指针,指向转换后的 JavaScript 对象。

现在我来举几个实例说明它的使用:

实例 1:将数字转换为对象

假设在 JavaScript 中,我们有一个数字 3.14,我们想调用它的toFixed()方法,将它格式化为 2 位小数的字符串。在纯 JavaScript 操作中,你可以直接调用3.14.toFixed(2),而在 C++扩展中,如果你拥有的是一个napi_value类型的数字,你可能需要先将它转为对象才能对其进行操作。

在 C++扩展中的代码可能会像这样:

napi_status status;
napi_value number_value;
napi_value number_object;

// 假设number_value已经是一个包含数字3.14的napi_value

// 现在将这个数字强制转换为JS对象
status = napi_coerce_to_object(env, number_value, &number_object);

if (status == napi_ok) {
    // 转换成功,number_object现在是一个封装了数字3.14的JS对象
} else {
    // 处理错误情况
}

实例 2:将字符串转换为对象

同理,如果你有一个 JavaScript 的字符串,并且你希望以对象的形式工作于它,比如获取字符长度或者使用正则表达式,则可以使用napi_coerce_to_object将其转为对象。

napi_status status;
napi_value string_value;
napi_value string_object;

// 假设string_value已经是一个包含字符串"hello"的napi_value

// 现在将这个字符串强制转换为JS对象
status = napi_coerce_to_object(env, string_value, &string_object);

if (status == napi_ok) {
    // 转换成功,string_object现在是一个封装了字符串"hello"的JS对象
} else {
    // 处理错误情况
}

总之,napi_coerce_to_object函数在你需要将 N-API 中的基本类型转换为 JavaScript 对象时非常有用,特别是当你需要对这些值执行一些只有对象才能执行的操作(如调用方法或访问属性)时。通过上述例子,我希望你能更好地理解这个函数的作用和如何在实际中使用它。

napi_coerce_to_stringopen in new window

在 Node.js 中,napi_coerce_to_string是 N-API 框架的一部分,N-API 是一个用于构建本地插件的 API。napi_coerce_to_string这个函数被用来将其他类型的变量转换为字符串类型。

简单来说,当你在编写本地插件时,可能会遇到需要处理来自 JavaScript 层面的各种类型的数据,例如数字、布尔值或者对象。有时候,为了操作方便或者满足特定的需求,你需要将这些数据转换成字符串。

napi_coerce_to_string函数就是用于执行这种转换的。当你调用这个函数,并传入一个 N-API 值(代表任意 JavaScript 值),它会尝试把这个值转换为一个 N-API 字符串。

下面是一个使用napi_coerce_to_string的例子:

假设我们正在编写一个 Node.js 本地插件,该插件提供了一个方法printValueAsString,这个方法接收一个参数,并将其打印出来,但是无论传入参数的实际类型如何,都要作为字符串进行打印。

##include `<`node_api.h>

// 这是我们将要注册的Native方法
napi_value PrintValueAsString(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value result;

    // 获取JavaScript传递给该方法的参数
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 将参数强制转换为字符串
    napi_coerce_to_string(env, args[0], &result);

    // 假设我们想要打印转换后的字符串
    // 通常情况下我们需要进一步从napi_value提取C字符串,然后使用printf

    return result; // 返回转化后的字符串
}

// 初始化函数,注册我们的native方法
napi_value Init(napi_env env, napi_value exports) {
    napi_value fn;

    // 创建一个新的函数
    napi_create_function(env, NULL, 0, PrintValueAsString, NULL, &fn);

    // 设置导出对象的属性
    napi_set_named_property(env, exports, "printValueAsString", fn);

    return exports;
}

// 注册模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在 JavaScript 中使用这个插件可能看起来像这样:

const nativeAddon = require("./build/Release/native-addon.node");

console.log(nativeAddon.printValueAsString(123)); // 打印: '123'
console.log(nativeAddon.printValueAsString(true)); // 打印: 'true'
console.log(nativeAddon.printValueAsString({ foo: "bar" })); // 打印: '[object Object]' 或者更具体的字符串表示,这取决于对象的toString()方法

在上述例子中,不管我们传给printValueAsString函数什么类型的参数,它都会尝试将其转换为字符串再返回。这就是napi_coerce_to_string在实际应用中的一个典型用法。

napi_typeofopen in new window

napi_typeof 是 Node.js 中的一个函数,属于 N-API(Node.js API)的一部分。N-API 是一个用来构建原生插件的 API,它允许你使用 C 或 C++ 代码编写可直接与 JavaScript 交互的扩展。

在解释 napi_typeof 之前,我们得先理解 JavaScript 中的类型系统。JavaScript 是一种动态类型语言,这意味着变量没有固定的类型,同一个变量可以被赋予不同类型的值。例如,一个变量可以开始时是一个数字,然后又被赋值为字符串。但在底层,JavaScript 引擎需要知道一个值具体是什么类型的数据。

当开发者使用 N-API 编写原生插件时,他们经常需要知道从 JavaScript 代码传递过来的值是什么类型。这时候 napi_typeof 函数就派上用场了。这个函数可以帮助开发者确定一个给定的 napi_value(表示 JavaScript 值的 C 类型)是哪种 JavaScript 数据类型。

下面是几个数据类型的例子:

  • Number: 表示数字,比如 423.14159
  • String: 表示文本,比如 "hello world"
  • Boolean: 表示逻辑真或假,即 truefalse
  • Object: 表示对象,可以包含多个键值对。
  • Function: 表示函数,可以被调用。

napi_typeof 函数正是用来检查这些类型。

举个实际例子,假设你正在编写一个原生插件,该插件提供了一个函数,这个函数接收一个参数,并根据这个参数的类型执行不同的操作:

##include `<`node_api.h>

// 这是一个 N-API 调用的例子
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];

    // 获取 JavaScript 传入的参数
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查参数的类型
    napi_valuetype arg_type;
    napi_typeof(env, args[0], &arg_type);

    if (arg_type == napi_string) {
        // 参数是字符串
        // ... 处理字符串逻辑 ...
    } else if (arg_type == napi_number) {
        // 参数是数字
        // ... 处理数字逻辑 ...
    }
    // ... 其他类型的处理 ...

    // 返回一个 JavaScript 的 undefined 值
    napi_value undefined_value;
    napi_get_undefined(env, &undefined_value);
    return undefined_value;
}
//文書は桜茶から来ています。商用目的では使用しないでください。
// 注册函数到 N-API
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

在上面的代码中,我们首先通过 napi_get_cb_info 获取到 JavaScript 传递给这个函数的参数。然后我们使用 napi_typeof 来判断这个参数的类型是否为字符串(napi_string)或数字(napi_number)。根据参数的类型,我们可以决定执行不同的代码逻辑。

最后,我们的函数返回了一个 undefined 值给 JavaScript 环境,因为我们没有指定其他的返回值。

通过这样的方式,napi_typeof 让我们能够在原生插件中更好地与 JavaScript 交互,并安全地处理不同类型的数据。

napi_instanceofopen in new window

napi_instanceof 是 Node.js 中的一个 N-API 函数,它用来判断一个对象是否是某个构造函数的实例。N-API 是一个 C 层面的 API,允许原生插件与 Node.js 进行交互,不受到特定版本 Node.js 的限制。

要理解 napi_instanceof,首先得知道 JavaScript 中的 instanceof 操作符做什么。在 JavaScript 中,我们使用 instanceof 来检查一个对象是否为特定构造函数创建的实例,例如:

function Car(make, model) {
  this.make = make;
  this.model = model;
}

const myCar = new Car("Toyota", "Corolla");

console.log(myCar instanceof Car); // 输出:true
console.log(myCar instanceof Object); // 输出:true
console.log(myCar instanceof Array); // 输出:false

在上面的代码中,myCar 是使用 Car 构造函数创建的,所以 myCar instanceof Car 返回 true。由于所有的 JavaScript 对象都默认继承自 ObjectmyCar instanceof Object 也返回 true。但是 myCar 不是数组,所以 myCar instanceof Array 返回 false

现在,当我们使用 Node.js 的 N-API 编写本地插件时,我们会用到 C 或者 C++。在这些语言中,你不能直接使用 JavaScript 的 instanceof 操作符。而是需要使用 napi_instanceof 函数来完成相同的任务。

napi_instanceof 函数的原型如下:

napi_status napi_instanceof(napi_env env,
                            napi_value object,
                            napi_value constructor,
                            bool* result);

这里的参数含义如下:

  • env: napi_env 类型,代表 Node.js 的运行环境,通过这个环境可以进行 API 调用。
  • object: napi_value 类型,要检查的 JavaScript 对象。
  • constructor: napi_value 类型,用作比较的构造函数。
  • result: bool* 类型,指向布尔值的指针,函数调用后,该位置的值会更新为检查结果(truefalse)。

当你调用 napi_instanceof 后,如果 objectconstructor 的实例,result 所指向的布尔值将被设置为 true,否则为 false

让我们举一个简单的例子,假设我们已经有了 Car 构造函数和一个 carObject 实例,我们想在 C/C++ 的 N-API 插件中检查它:

##include `<`node_api.h>

// 假设在某处定义了 carObject 和 CarConstructor 的 napi_value

bool isInstanceOf;
napi_status status = napi_instanceof(env, carObject, CarConstructor, &isInstanceOf);

if (status == napi_ok && isInstanceOf) {
    // carObject 是 CarConstructor 创建的实例
} else {
    // carObject 不是 CarConstructor 创建的实例或者检查失败
}

在上述 C 代码中,我们检查 carObject 是否是 CarConstructor 的实例,并根据结果执行相应操作。

总结一下,napi_instanceof 允许你在编写 Node.js 原生插件时,在 C/C++ 代码中执行与 JavaScript 中 instanceof 类似的操作。通过 N-API,你能够安全地进行类型检查,从而保证你的插件可以正确处理传入的 JavaScript 对象。

napi_is_arrayopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。原生插件是指直接使用 C、C++等语言编写,然后可以在 Node.js 代码中调用的模块。这使得开发者能够在性能关键的场合下利用本地库的高效性能。

napi_is_array 是 N-API 的一部分,它提供了一种方法来确定一个 napi_value 是否表示一个 JavaScript 数组。napi_value 是 N-API 中用来表示 JavaScript 值的一个抽象类型,不管这个值是数字、对象、函数还是数组。

下面的步骤将会详细介绍如何在原生插件中使用 napi_is_array 函数,并且给出一个简单的例子:

使用 napi_is_array 函数的步骤

  1. 获取 napi_env:为了使用任意 N-API 函数,你需要有一个 napi_env 环境变量。这个环境变量通常是作为原生函数的参数传入的,代表当前的 Node.js 运行时环境。

  2. 准备 napi_value:你需要拥有或得到一个 napi_value,它代表想要检查的 JavaScript 值。

  3. 调用 napi_is_array:使用 napi_is_array 来判断这个 napi_value 是否表示一个数组。函数会返回一个布尔值来告知你结果。

  4. 处理结果:根据返回的布尔值,执行相应的逻辑。

实际运用的例子

假设我们正在编写一个原生插件的函数,该函数需要检查传入的 JavaScript 参数是否为数组。

##include `<`node_api.h>

// 定义一个原生函数,它接收一个参数并判断这个参数是否为数组。
napi_value IsArray(napi_env env, napi_callback_info info) {
    // 定义必要的变量
    napi_value argv[1]; // 用于存放参数的数组
    size_t argc = 1;    // 参数的数量
    bool is_array = false;
    napi_value result;

    // 解析参数
    napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    // 调用 napi_is_array 判断第一个参数是否为数组
    napi_is_array(env, argv[0], &is_array);

    // 根据 is_array 的值创建一个 JavaScript 布尔值
    napi_get_boolean(env, is_array, &result);

    // 返回这个布尔值
    return result;
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    // 定义一个属性描述符,让 JavaScript 能够调用 IsArray 函数
    napi_property_descriptor desc = { "isArray", 0, IsArray, 0, 0, 0, napi_default, 0 };

    // 将 IsArray 函数绑定到导出对象上
    napi_define_properties(env, exports, 1, &desc);

    return exports;
}

// 定义模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

上面的 C 代码示例定义了一个名为 IsArray 的函数,它检查传入的参数是否为数组,并返回 true 或 false。这个函数之后被暴露给 JavaScript 代码,因此 JavaScript 代码可以像调用普通的 JavaScript 函数那样调用它。

JavaScript 代码可能长这样:

const nativeAddon = require("./build/Release/native-addon.node");

console.log(nativeAddon.isArray([1, 2, 3])); // 输出:true
console.log(nativeAddon.isArray({ foo: "bar" })); // 输出:false

在 JavaScript 代码中,我们首先通过 require 引入了构建好的原生插件模块。然后我们调用了 nativeAddon.isArray 方法,并向它传递了不同类型的参数进行测试。

通过这样的方式,我们实现了从 JavaScript 到原生代码再回到 JavaScript 的通信和操作,而 napi_is_array 在此过程中扮演了判断数组的关键角色。

napi_is_arraybufferopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它允许你在服务器端运行 JavaScript 代码。N-API(Node.js API)是 Node.js 提供的一套用于构建原生插件的 API,使得你可以用 C 或 C++ 语言来写扩展模块,并且这些模块在 Node.js 的不同版本中保持兼容。

napi_is_arraybuffer 是 N-API 中的一个函数,它用来检查一个给定的 napi_value 是否是一个 ArrayBuffer。ArrayBuffer 是 JavaScript 中的一个全局对象,用于表示通用的、固定长度的原始二进制数据缓冲区。你不能直接操作 ArrayBuffer 的内容;相反,你使用类型化数组或 DataView 对象来处理原始缓冲区的数据。

这里是 napi_is_arraybuffer 函数的简要说明:

  • 参数:该函数接受两个参数。第一个参数是环境句柄 env,它代表了当前的 N-API 调用环境。第二个参数 value 是要检查的 napi_value
  • 返回值:返回一个布尔值,通过第三个参数 result 返回。如果 value 是一个 ArrayBuffer,则 *result 设置为 true;否则设置为 false

实际应用示例:

假设你正在编写一个 Node.js 原生插件,需要检查从 JavaScript 传递过来的参数是否是一个 ArrayBuffer。

C/C++ 代码示例:

##include `<`node_api.h>

// ... 其他必要的初始化代码 ...

napi_value CheckIfArrayBuffer(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1];
  napi_value returnValue;
  bool is_arraybuffer;

  // 解析参数
  napi_get_cb_info(env, info, &argc, args, NULL, NULL);

  // 检查 args[0] 是否是 ArrayBuffer
  napi_status status = napi_is_arraybuffer(env, args[0], &is_arraybuffer);

  if (status == napi_ok && is_arraybuffer) {
    // 是 ArrayBuffer
    napi_get_boolean(env, true, &returnValue);
  } else {
    // 不是 ArrayBuffer
    napi_get_boolean(env, false, &returnValue);
  }

  return returnValue;
}

// ... 注册函数等其他代码 ...

上述代码中的 CheckIfArrayBuffer 函数会被注册到 Node.js 环境中,并由 JavaScript 调用。它会检查传入的参数是否是 ArrayBuffer 并返回相应的布尔值。

JavaScript 调用示例:

const nativeAddon = require("./build/Release/native-addon.node");

let buffer = new ArrayBuffer(10);
let notBuffer = {};

console.log(nativeAddon.CheckIfArrayBuffer(buffer)); // 输出应该是 true
console.log(nativeAddon.CheckIfArrayBuffer(notBuffer)); // 输出应该是 false

请注意,可能需要安装和配置额外的工具和库才能编译和构建原生插件,比如 node-gyp。上面的例子只是展示了如何使用 napi_is_arraybuffer 函数,没有包括全部必要的构建步骤。

napi_is_bufferopen in new window

napi_is_buffer 是 Node.js 中的一个 N-API 函数,用来判断传入的 napi_value 是否是一个 Node.js Buffer 对象。Node.js Buffer 类是用来处理二进制数据流的类,在 Node.js 中经常被用来读写数据,比如文件、网络通信等。

在了解 napi_is_buffer 之前,首先要知道 N-API(Node.js API)是 Node.js 提供的一套用 C 或 C++ 编写扩展的 API,这些 API 是独立于 V8 引擎,可以使得编写的本地插件可以跨不同版本的 Node.js 运行。

现在,我们来具体看一下 napi_is_buffer 函数:

函数原型如下:

napi_status napi_is_buffer(napi_env env, napi_value value, bool* result);
  • env 是表示当前执行环境的句柄。
  • value 是你想要检测是否为 Buffer 的 JavaScript 值。
  • result 是一个指向布尔值的指针,函数会将检测结果存储在这个布尔值中。

当你调用 napi_is_buffer 后,如果 value 是一个 Buffer 对象,那么在 result 指向的布尔值会被设置为 true,否则会被设置为 false

这里是一个简化的例子,展示了如何在一个 N-API 扩展中使用 napi_is_buffer

##include `<`node_api.h>

//... 在其他代码中

void SomeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    napi_value arg = args[0];
    bool isBuffer;
    napi_status status = napi_is_buffer(env, arg, &isBuffer);

    if (status != napi_ok) {
        // 处理可能发生的错误
    }

    if (isBuffer) {
        // 如果是 Buffer, 则执行相关操作
    } else {
        // 如果不是 Buffer, 则执行其他逻辑或返回错误
    }
}

//... 其他代码和模块导出逻辑

在上述例子中,SomeFunction 作为一个 N-API 函数可能会被 JavaScript 调用,它接收参数并判断第一个参数是否是 Buffer。如果是 Buffer 类型,你可以进行如读取数据、处理二进制信息等操作;如果不是,则可以选择抛出错误或者进行其它逻辑处理。

在实际应用中,可能要处理从文件读取数据、接收网络请求的数据等情况,这时候用 napi_is_buffer 来确保接收到的数据是正确的 Buffer 对象非常有用。这样的检查能够让你的本地扩展更加健壮,避免因类型错误而引发的问题。

napi_is_dateopen in new window

napi_is_date 是 Node.js 中的一个函数,这个函数属于 N-API(Node API),一个用来构建原生插件的接口。N-API 提供了一套与 JavaScript 运行时相关的 API,这些 API 允许你创建可以直接与 Node.js 交互的本地插件。

在 Node.js 中,JavaScript 可以使用 Date 对象来处理时间和日期。但是当我们在 C 或 C++ 的原生插件代码中工作时,需要一种方法来判断一个从 JavaScript 传递过来的值是否为一个 Date 对象。这就是 napi_is_date 函数的用途。

简单地说,napi_is_date 函数能够帮助你检查一个传入的 napi_value(这是一个代表 JavaScript 值的抽象类型)是否是一个 JavaScript Date 对象。

下面是如何使用 napi_is_date 函数的例子:

假设你正在编写一个原生模块,需要判断 JavaScript 传递的参数是否是一个 Date 对象:

##include `<`node_api.h>

// 这个函数将会被绑定到 JavaScript 中,并可从那里调用
napi_value IsDateExample(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 1;
    napi_value args[1];
    bool is_date;

    // 获取传递给该函数的 JavaScript 参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 判断第一个参数是否为 Date 类型
    status = napi_is_date(env, args[0], &is_date);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 将结果转换回 JavaScript 的布尔值并返回
    napi_value result;
    status = napi_get_boolean(env, is_date, &result);
    if (status != napi_ok) {
        // 处理错误...
    }

    return result; // 返回结果给 JavaScript 调用方
}

// 初始化模块,注册 IsDateExample 函数
NAPI_MODULE_INIT() {
    napi_status status;
    napi_value fn;

    // 创建一个新的函数对象
    status = napi_create_function(env, NULL, 0, IsDateExample, NULL, &fn);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 将这个函数设置为导出的属性
    status = napi_set_named_property(env, exports, "isDateExample", fn);
    if (status != napi_ok) {
        // 处理错误...
    }

    return exports;
}

然后在 JavaScript 中,你可以像下面这样调用此函数:

const nativeAddon = require("./build/Release/native-addon");

const date = new Date();
const notADate = {};

console.log(nativeAddon.isDateExample(date)); // 应该输出:true
console.log(nativeAddon.isDateExample(notADate)); // 应该输出:false

在上述的 C 代码示例中,函数 IsDateExample 会接收一个参数,然后利用 napi_is_date 来检查这个参数是否为一个 JavaScript 的 Date 对象。如果是,它会返回 true,否则返回 false。而在 JavaScript 代码中,我们通过加载这个原生模块来调用 isDateExample 方法,分别传入真正的 Date 对象以及一个普通对象,然后打印出结果。

napi_is_erroropen in new window

napi_is_error 是 Node.js 中的一个函数,它属于 N-API(Node API),这是一组用于构建原生插件的 C API。原生插件是用 C、C++等语言编写的模块,它们可以直接与 Node.js 的 V8 引擎交互,提供比 JavaScript 更高效的性能,或实现一些在纯 JavaScript 中不可能或困难的功能。

具体来说,napi_is_error 函数用于检查一个 napi_value 是否表示一个 JavaScript 错误对象。在 Node.js 中,JavaScript 错误对象通常用于捕获和处理运行时错误。

参数解释

  • env: 表示当前的 napi 环境,它是一个上下文对象,包含了当前正在运行的 Node.js 实例的信息。
  • value: 是要检查的 napi_value,即你想知道它是否为一个错误对象的值。
  • result: 这是一个指向布尔值的指针,函数将在这里设置结果。如果 value 是一个错误对象,则这个布尔值会被设置为 true;否则,它会被设置为 false

使用场景举例

假设你正在编写一个原生插件的功能,该功能调用了另一个可能会抛出错误的函数。在返回给 JavaScript 之前,你想检查结果是否是一个错误对象,并据此采取不同的行动。

##include `<`node_api.h>

// 假设有一个函数可能会产生一个错误对象
napi_value SomeFunctionThatMayThrow(napi_env env) {
    // ... 一些操作可能会失败并返回错误对象
}

void MyNativeFunction(napi_env env, napi_callback_info info) {
    // 调用可能会抛出错误的函数
    napi_value result = SomeFunctionThatMayThrow(env);

    // 检查结果是否是一个错误对象
    bool is_error;
    napi_status status = napi_is_error(env, result, &is_error);

    if (status == napi_ok && is_error) {
        // 如果是错误对象,处理错误情况
        // ...
    } else {
        // 如果不是错误对象,继续正常逻辑
        // ...
    }
}

在上面的代码中,我们先定义了一个函数 SomeFunctionThatMayThrow,它在某些情况下可能会返回一个错误对象。然后,我们有了另一个函数 MyNativeFunction,它调用了 SomeFunctionThatMayThrow 并使用 napi_is_error 来检查返回的值是否是一个错误对象。根据检查结果,我们可以决定如何处理这种情况:如果是一个错误对象,我们可以采取适当的错误处理措施,否则,我们继续正常的逻辑处理。

需要注意的是,napi_is_error 本身也会返回一个状态码(napi_status),如果调用成功且未发生错误,则返回 napi_ok。所以在检查是否为错误对象之前,我们还应该检查 napi_is_error 调用自身是否成功。

napi_is_typedarrayopen in new window

napi_is_typedarray 是 Node.js 中的一个函数,它属于 N-API(Node API),这是 Node.js 提供的一个用于构建原生插件的 API。N-API 是一个稳定且与 Node.js 版本无关的 C 级别的 API,使得开发者能够不用担心 Node.js 升级后导致的插件兼容性问题。

在解释 napi_is_typedarray 之前,我们先来了解一下什么是 TypedArray。TypedArray 是 JavaScript 中的一种特殊类型的数组,它提供了一种读写二进制数据缓冲区的机制。和普通的 JavaScript 数组不同,TypedArray 工作在 ArrayBuffer 上,并且其元素类型是固定的,比如 Int8Array, Uint8Array, Float32Array 等。

现在,让我们看看 napi_is_typedarray

napi_is_typedarray

这个函数用来检查给定的 N-API 值是否为一个 TypedArray。如果是,这个函数会返回 true,否则返回 false

参数

  1. env: 这是当前执行环境的句柄,它提供了 Node.js 环境的上下文。
  2. value: 这是你想要检查的 N-API 值。
  3. result: 这是一个布尔值的指针,函数通过它返回检查结果。

返回值

如果调用成功,该函数会返回 napi_ok,表示没有错误发生。因此,你需要检查result指向的布尔值来确定给定的值是否为 TypedArray。

实际运用的例子

设想你正在编写一个 Node.js 的原生插件,这个插件需要处理图像或音频数据。你可能会从 JavaScript 代码接收一个数组,而你希望确认它是一个特定类型的 TypedArray,这样就可以以二进制形式直接读取数据,而不需要额外的类型转换。你可以使用 napi_is_typedarray 来检查这一点。

这里是一个虚构的例子代码片段,展示了如何在一个原生插件中使用 napi_is_typedarray

##include `<`node_api.h>

// ... 其他必需的头文件和代码

napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查是否只传递了一个参数并且这个参数是 TypedArray
    bool isTypedArray = false;
    napi_status status = napi_is_typedarray(env, args[0], &isTypedArray);

    if (status != napi_ok || !isTypedArray) {
        // 处理错误或抛出异常
    }

    // 如果是 TypedArray,执行后续的操作...
}

// ... 其他必需的代码来注册这个函数

在这个例子中,我们定义了一个名为 MyNativeFunction 的原生函数,它检查传递给它的第一个参数是否为 TypedArray。首先,我们使用 napi_get_cb_info 获取参数,然后调用 napi_is_typedarray 来进行检查。如果这个参数确实是 TypedArray,那么我们将继续执行所需的操作;如果不是,我们将处理错误或抛出异常。

总结起来,napi_is_typedarray 是 N-API 下用来检查一个值是否为 TypedArray 的工具函数,对于处理二进制数据的原生模块开发非常有用。

napi_is_dataviewopen in new window

napi_is_dataview是 Node.js 中一个 C API 的函数,这个 API 属于 N-API,是 Node.js 用来构建原生插件的一个接口。N-API 提供了一套与 JavaScript 引擎无关的 C 语言 API,让开发者能够创建可以在不同版本的 Node.js 中运行而不需要重新编译的原生插件。

在解释napi_is_dataview之前,我首先简单介绍一下 DataView。在 JavaScript 中,DataView 是一种用于操作二进制数据缓冲区(ArrayBuffer)内容的低级接口。它允许你在 ArrayBuffer 中读取和写入多种数值类型,无论它们的字节顺序是大端(big endian)还是小端(little endian)。

好了,接下来让我们看看napi_is_dataview

napi_is_dataview

这个函数用来检查某个 N-API 值是否是一个 DataView 对象。其作用类似于 JavaScript 中的instanceof操作符,但是它是用在原生插件的 C 代码中。

参数:

  • env: 这是表示当前执行环境的napi_env
  • value: 要检查的 N-API 值。
  • result: 一个指向布尔值的指针,用于存储检查结果:如果value是 DataView,则设为true;否则,设为false

返回值:

这个函数返回一个napi_status值,表示操作成功或失败的状态。如果函数调用成功,你可以检查result指向的值来确定传入的value是否为 DataView。

例子

假设你正在编写一个 Node.js 原生插件,并且你想确定从 JavaScript 传递给你的 N-API 函数的一个参数是否是一个 DataView。

##include `<`node_api.h>

// 假设这是一个原生函数,将被JavaScript调用
napi_value MyFunction(napi_env env, napi_callback_info info) {
  size_t argc = 1;
  napi_value args[1];
  napi_get_cb_info(env, info, &argc, args, NULL, NULL);

  // 检查第一个参数是否是DataView
  bool is_data_view;
  napi_is_dataview(env, args[0], &is_data_view);

  if (is_data_view) {
    // 参数是DataView,处理DataView逻辑...
  } else {
    // 参数不是DataView,可能抛出错误或者进行其他处理...
  }

  // ...函数的其他逻辑...

  napi_value result;
  // 假设创建一个返回值
  napi_get_undefined(env, &result);
  return result;
}

在上面的例子中,MyFunction是一个原生函数,它期望接收一个 DataView 作为参数。我们使用napi_get_cb_info取得传递给函数的参数,然后使用napi_is_dataview来检查这个参数是否确实是一个 DataView。根据检查结果,我们可以决定如何处理这个参数。

记得在实际代码中,你应该总是检查napi_is_dataview的返回值以确定操作是否成功,并相应地处理错误情况。

napi_strict_equalsopen in new window

Node.js 中的 napi_strict_equals 是一个函数,它属于 N-API(Node.js API)的一部分。N-API 是 Node.js 提供的用于构建原生插件的一套稳定的 C API。原生插件是指用 C 或 C++ 写的模块,它们可以被 JavaScript 代码直接调用。这些功能对于提升性能和/或使 Node.js 能够访问操作系统级别的功能非常有用。

现在,来具体看看 napi_strict_equals 这个函数。如名称所示,该函数用于比较两个 JavaScript 值是否严格相等,这意味着它不会进行类型转换。在 JavaScript 中,使用 === 操作符可以执行严格相等性检查。而 napi_strict_equals 则是这种操作的 C 层面的等效实现。

为什么我们需要这样的函数?当你编写原生模块时,可能需要判断 JavaScript 传入的参数是否与某个特定值完全相同。由于你在 C 或 C++ 的环境中工作,所以不能直接使用 JavaScript 的 ===。这时候,napi_strict_equals 就成了判断这种严格相等性的工具。

下面是一个简化的例子来展示 napi_strict_equals 的基本用法:

##include `<`node_api.h>

// 假设有一个函数叫做 "StrictEquals" 绑定到了 JS
napi_value StrictEquals(napi_env env, napi_callback_info info) {
    // 获取函数参数
    size_t argc = 2;
    napi_value args[2];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查参数数量是否正确
    if (argc `<` 2) {
        napi_throw_type_error(env, NULL, "Function expects two arguments.");
        return NULL;
    }

    // 使用 napi_strict_equals 比较两个参数是否严格相等
    bool result;
    napi_strict_equals(env, args[0], args[1], &result);

    // 把结果转换为 JavaScript 的布尔值返回给 JS
    napi_value jsResult;
    napi_get_boolean(env, result, &jsResult);

    return jsResult;
}

void Initialize(napi_env env, napi_value exports) {
    // ... 初始化代码,并把上面的函数绑定给 JS 调用 ...
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Initialize)

在这个例子中,我们定义了一个名为 StrictEquals 的函数,它接受两个参数,并使用 napi_strict_equals 来判断它们是否严格相同。函数的结果是一个布尔值,表示两个参数是否相等。

在 JavaScript 端,你可以像这样使用这个函数:

const nativeModule = require("./build/Release/native-module.node");

console.log(nativeModule.StrictEquals(5, 5)); // 输出 true
console.log(nativeModule.StrictEquals("5", 5)); // 输出 false,因为类型不同

在上面的 JavaScript 代码中,我们通过 require 引入了 C/C++ 编写并编译好的原生模块,然后使用 .StrictEquals 函数来进行严格比较操作。

总结一下,napi_strict_equals 在 Node.js 原生模块开发中使用,它提供了一个在原生代码中执行严格相等性比较的能力。通过这种方式,你可以在扩展 Node.js 功能时对 JavaScript 值进行更精确的操作。

napi_detach_arraybufferopen in new window

napi_detach_arraybuffer 是 Node.js 中的一个 N-API 函数,它用于管理 JavaScript ArrayBuffer 对象与其底层的二进制数据之间的关联。在解释这个函数之前,我会先简单介绍一些相关概念。

ArrayBuffer 简介

ArrayBuffer 是 JavaScript 中的一种数据类型,它表示一块固定大小的原始二进制数据缓冲区。你可以通过 Typed Arrays 或 DataView 来读写 ArrayBuffer 中的数据。这对于处理如文件数据、网络通信等需要高效操作二进制数据的场景特别有用。

N-API 简介

N-API 是 Node.js 提供的一组 C API,允许本地插件(用 C 或 C++编写的模块)与 JavaScript 代码无缝交互,同时保持与 Node.js 版本的向后兼容性。本地插件可以利用 N-API 创建和操作 JavaScript 值,并通过它们扩展 Node.js 的功能。

napi_detach_arraybuffer

当我们在本地插件中使用 ArrayBuffer 时,可能会出现一种情况:我们希望把 ArrayBuffer 中的数据「分离」出来。也就是说,让 ArrayBuffer 不再引用那部分内存。这样,JavaScript 的垃圾回收机制就不会管理那部分内存了,而是留给我们自己管理。

在这种情况下,就可以使用 napi_detach_arraybuffer 函数。这个函数将 ArrayBuffer 对象标记为 "detached" 状态,即该 ArrayBuffer 不再指向任何的数据。这样做的一个常见理由是为了避免数据的重复拷贝,提升性能:我们可能先通过 JavaScript 创建了一个 ArrayBuffer,然后传递到本地插件进行填充;填充完毕后,为了让其他原生库直接操作这块内存,我们就会将其分离。

实际例子

假设你在开发一个图像处理的 Node.js 插件。用户将通过 JavaScript 创建一个 ArrayBuffer,期望你的插件将图像数据填充进去。一旦你完成了填充,你可能想要调用一个专门的图像处理库,它并不知道 JavaScript 的存在,只会处理纯粹的内存数据。

以下是一个简化的代码流程:

  1. 用户在 JS 侧创建 ArrayBuffer。
  2. 用户调用插件的方法,将 ArrayBuffer 传入。
  3. 插件接收 ArrayBuffer,并使用 napi_create_external_arraybuffer 或其他方法来关联本地数据。
  4. 插件执行操作,比如填充图像数据。
  5. 插件调用 napi_detach_arraybuffer 来分离 ArrayBuffer,这样图像处理库就可以安全地处理这块内存了。
##include `<`node_api.h>

// 假设此函数由图像处理库提供,用于处理内存中的图像数据
void process_image_data(void* data, size_t length);

napi_value DetachArrayBuffer(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 分离 ArrayBuffer
    napi_detach_arraybuffer(env, args[0]);

    // 获取 ArrayBuffer 的数据指针
    void* data;
    size_t length;
    napi_get_arraybuffer_info(env, args[0], &data, &length);

    // 调用图像处理函数
    process_image_data(data, length);

    return NULL;
}

// ... 其余插件初始化代码 ...

请注意,这里的例子非常简化,实际使用中还需要进行错误检查和资源管理等工作。但它展示了 napi_detach_arraybuffer 在实践中是如何被用来优化性能和与本地库交互的。

napi_is_detached_arraybufferopen in new window

napi_is_detached_arraybuffer 是 Node.js 中 N-API (Native API) 的一部分,它是一个函数,让你可以检查一个 ArrayBuffer 是否已经被 "detach"(分离)。在 JavaScript 中,ArrayBuffer 是一种用来表示一块原始二进制数据的方法;你可以想象成它就像是一个可以存储字节的容器。

在某些情况下,特别是在与 WebAssembly 或者其他底层操作打交道时,可能会需要 "detach" 一个 ArrayBuffer。"detach" 操作基本上是指使这个 ArrayBuffer 的内容变得不可访问,这样做的一个原因可能是为了释放内存或者防止后续的 JavaScript 代码错误地使用了这段内存。

现在,让我们通过几个简单的例子来理解 napi_is_detached_arraybuffer 函数的作用。

实际运用的例子

注意: 下面的代码是在 Node.js 的 C/C++ 插件中使用的,而非纯 JavaScript 环境。

  1. 检查 ArrayBuffer 是否已经 Detach

假设你正在编写一个 Node.js 的扩展模块,你有一个从 JavaScript 传递过来的 ArrayBuffer 对象,你想检查它是否已经被 detach。

##include `<`node_api.h>

// 这个函数将被暴露给 JavaScript,用于检查 ArrayBuffer 是否 detach
napi_value IsDetached(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查参数数量是否正确
    if (argc `<` 1) {
        napi_throw_type_error(env, NULL, "Function expects an ArrayBuffer.");
        return NULL;
    }

    napi_value arrayBuffer = args[0];
    bool is_detached = false;

    // 调用 napi_is_detached_arraybuffer 来检查 ArrayBuffer 是否 detach
    napi_status status = napi_is_detached_arraybuffer(env, arrayBuffer, &is_detached);

    if (status != napi_ok) {
        napi_throw_error(env, NULL, "Failed to check if ArrayBuffer is detached.");
        return NULL;
    }

    napi_value result;
    napi_get_boolean(env, is_detached, &result);

    return result;
}

// 模块初始化代码...

在上面的例子中,我们定义了一个 IsDetached 函数,这个函数可以被 JavaScript 调用,并且它接受一个 ArrayBuffer 参数,然后使用 napi_is_detached_arraybuffer 检查该 ArrayBuffer 是否已经被 detach。函数返回一个布尔值,告诉调用者 ArrayBuffer 是否 detach。

  1. 在实际操作前确认 ArrayBuffer 的状态

在处理 ArrayBuffer 进行内存操作之前,先确认它没有被 detach 是一个好习惯,可以避免潜在的错误:

// 假设你有一个用于处理 ArrayBuffer 数据的函数
void ProcessArrayBufferData(napi_env env, napi_value arrayBuffer) {
    bool is_detached = false;
    napi_is_detached_arraybuffer(env, arrayBuffer, &is_detached);

    if (is_detached) {
        // 如果 ArrayBuffer 已经被 detach,则不进行操作
        // 可以抛出错误或者直接退出
        napi_throw_error(env, NULL, "The ArrayBuffer is detached and cannot be used.");
        return;
    }

    // 正常处理 ArrayBuffer 数据...
}

在这个例子中,我们在对 ArrayBuffer 进行任何处理之前,先使用 napi_is_detached_arraybuffer 函数检查它的状态。如果发现已经 detach,就不再进行后续操作,以此来保证程序的安全性和稳定性。

希望这些例子能够帮助你理解 napi_is_detached_arraybuffer 在 Node.js N-API 中的使用和重要性。在编写涉及原始二进制数据操作的扩展模块时,正确检查 ArrayBuffer 的状态是非常关键的。

Working with JavaScript propertiesopen in new window

当我们在 Node.js 中使用 N-API(Node.js 的原生 API 接口)与 JavaScript 属性打交道时,我们主要关注几个方面:创建属性、获取属性值、设置属性值以及操作对象的属性。我会用简单的例子来解释这些概念。

创建属性

在 JavaScript 中,你可以直接为对象添加新属性。例如:

let obj = {};
obj.myProperty = 123;

但是在使用 N-API 的 C/C++ 插件中,你需要通过特定的函数来实现类似的功能。以下是一个如何在 N-API 中创建属性的例子:

napi_status status;
napi_value my_property_name;
napi_value my_property_value;
napi_value object;

// 假设 env 和 object 已经被正确初始化。

// 创建一个 JavaScript 字符串作为属性名。
status = napi_create_string_utf8(env, "myProperty", NAPI_AUTO_LENGTH, &my_property_name);

// 创建一个 JavaScript 数字作为属性值。
status = napi_create_double(env, 123, &my_property_value);

// 将创建的属性和值添加到对象上。
status = napi_set_property(env, object, my_property_name, my_property_value);

获取属性值

在 JavaScript 中,要获取对象属性的值非常直接,就像这样:

let obj = { myProperty: 123 };
let value = obj.myProperty;
console.log(value); // 输出: 123

在 N-API 中,获取属性也需要调用特定的函数:

napi_status status;
napi_value my_property_value;
napi_value object;
napi_value result;

// 假设 env 和 object 已经被正确初始化,并且object上有myProperty属性。

// 获取 myProperty 属性的值。
status = napi_get_named_property(env, object, "myProperty", &result);

// 假设属性值是一个数字,转换结果并输出。
double value;
status = napi_get_value_double(env, result, &value);
printf("The property value is: %f\n", value); // 输出将类似于: The property value is: 123.000000

设置属性值

在 JavaScript 中修改属性值同样很简单:

let obj = { myProperty: 123 };
obj.myProperty = 456; // 修改属性值

在使用 N-API 时,我们可以如下设置属性值:

napi_status status;
napi_value my_property_value;
napi_value object;

// 假设 env 和 object 已经被正确初始化,并且object上有myProperty属性。

// 创建一个新的 JavaScript 数字作为新的属性值。
status = napi_create_double(env, 456, &my_property_value);

// 更新属性值。
status = napi_set_named_property(env, object, "myProperty", my_property_value);

操作对象的属性

在 JavaScript 中,你可以使用 delete 来删除属性,或者使用 Object.keys() 来列出对象的所有属性等。对于 N-API,我们同样有函数来处理这些操作。

删除属性的例子:

napi_status status;
napi_value object;
bool result;

// 假设 env 和 object 已经被正确初始化,并且object上有myProperty属性。

// 删除该属性。
status = napi_delete_property(env, object, "myProperty", &result);
// result 现在为 true 表示删除成功。

以上就是 Node.js 中 N-API 处理 JavaScript 属性的基础知识和一些简单例子。这些例子展示了如何在编写 C/C++ 插件时与 JavaScript 对象的属性进行交互。记得在实际的应用中,每次调用后都需要检查 napi_status 的返回值,确保操作成功。

Structuresopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行环境,使得开发者可以用 JavaScript 来编写后端代码。而 N-API(Node.js API)则是 Node.js 提供的一个用来构建本地插件的 API。

在理解 Node.js 的 N-API 文档中的 "Structures" 一节时,我们需要知道 "Structures" 在这里指的是 C 语言中的结构体(structs)。结构体是一种用户自定义的数据类型,允许你将多个不同的数据项组合成单个复合数据类型。在 N-API 中,这些结构体被用作和 JavaScript 运行时交互的接口。

下面是对 "Structures" 部分内容的简要解释和实际应用例子:

  1. napi_property_descriptor: 这是一个结构,在创建或定义对象的属性时使用。例如,如果你想向一个 JavaScript 对象添加一个新的属性或方法,你就会使用这个结构体。
// 定义一个 napi_property_descriptor
napi_property_descriptor desc = {
    "myProperty",  // 属性名
    NULL,
    MyPropertyGetter,  // getter 函数
    MyPropertySetter,  // setter 函数
    NULL,
    NULL,
    napi_default,
    NULL
};

在上面的代码中,MyPropertyGetterMyPropertySetter 是 C 函数,当从 JavaScript 访问或修改 myProperty属性时会调用它们。

  1. napi_callback_info: 当一个原生函数(即由 C/C++ 编写的函数)被 JavaScript 代码调用时,napi_callback_info 结构会包含调用的上下文信息,比如传递给函数的参数等。

  2. napi_ref: 这个结构代表了一个对 JavaScript 对象的强引用。如果你有一个 JavaScript 对象,并且你不希望它在某个时候被垃圾回收器回收,可以使用 napi_ref 来确保对象保持活跃。

  3. napi_value: 表示一个 ECMAScript(JavaScript 的规范)值。无论何时你需要从 C/C++ 代码与 JavaScript 更基本的数据类型(如字符串、数字、对象等)进行交云,都会用到 napi_value

例如,假设我们正在编写一个 Node.js 插件,该插件提供了一个名为 addNumbers 的函数,用于计算两个数的和:

##include `<`node_api.h>

// 原生 addNumbers 函数
napi_value AddNumbers(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 2;
    napi_value args[2];
    double value1, value2;

    // 获取 JavaScript 调用提供的参数
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 确保没有出错并且接受了两个参数
    if (status == napi_ok && argc == 2) {
        // 将 napi_values (JavaScript 值)转换为 C 类型的 double
        status = napi_get_value_double(env, args[0], &value1);
        status = napi_get_value_double(env, args[1], &value2);

        // 计算两数之和
        double sum = value1 + value2;
        napi_value result;

        // 将 C 类型的和转换回 napi_value
        status = napi_create_double(env, sum, &result);

        // 返回结果给 JavaScript
        return result;
    }

    // 如果出现错误,返回 undefined
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

// 模块初始化函数
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    // 创建一个表示 addNumbers 函数的 napi_value
    status = napi_create_function(env, NULL, 0, AddNumbers, NULL, &fn);

    // 把 addNumbers 函数作为模块导出的属性
    status = napi_set_named_property(env, exports, "addNumbers", fn);

    return exports;
}

// 注册模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中,我们首先定义了一个 AddNumbers 函数,它使用 napi_get_cb_info 来获取 JavaScript 传递的参数,并使用 napi_create_doublenapi_get_value_double 来在 JavaScript 的数值和 C 的数值之间进行转换。最后,我们通过 napi_create_function 创建函数并通过 napi_set_named_property 把它绑定到模块的导出对象上。

这样,当 JavaScript 代码通过 require 导入这个模块后,就能够调用 addNumbers 函数,并且在底层执行我们的 C 代码进行加法运算了。

napi_property_attributesopen in new window

Node.js 中的 N-API 是一个用于构建本机插件(native addons)的底层接口,这些本机插件是使用 C/C++编写的,用来直接与 Node.js 的 V8 引擎进行交互。napi_property_attributes是 N-API 中定义属性特性的枚举类型,用于设置 JavaScript 对象属性的一些特征。

在 JavaScript 中,对象的属性可以具有几个特征,比如它们是否可写(writable)、是否可枚举(enumerable)、是否可配置(configurable)。理解这些特征对于创建行为像普通 JavaScript 对象属性的本机插件非常重要。

napi_property_attributes枚举中包含以下值:

  • napi_default: 这是默认选项,等同于 JavaScript 中普通属性的默认行为,即属性是可写、可枚举、可配置的。
  • napi_readonly: 设置该属性标志后,属性将不可写,也就是说你不能更改属性的值。
  • napi_dont_enum: 设置该属性标志后,属性在对象的枚举属性列表中不会出现,也就是说它不会出现在for...in循环或Object.keys()的输出中。
  • napi_fixed: 相当于同时设置了napi_readonlynapi_dont_delete,使属性既不可写也不可删除。
  • napi_static: 表示属性是静态的。

让我们看看如何在实际中使用这些属性:

##include `<`node_api.h>

// 一个简单的函数,返回一个字符串
napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_value greeting;
    napi_create_string_utf8(env, "Hello World", NAPI_AUTO_LENGTH, &greeting);
    return greeting;
}

// 初始化函数,在模块加载时调用
napi_value Init(napi_env env, napi_value exports) {
    // 定义一个新的函数属性
    napi_property_descriptor desc = {
        "myFunction",             // 属性名称
        NULL,                     // 属性的唯一标识符(Symbol),这里没有使用
        MyFunction,               // 指向上面定义的函数的指针
        NULL,                     // getter 函数,这里不需要
        NULL,                     // setter 函数,这里不需要
        NULL,                     // value, 因为已经指定了方法,所以不需要设置值
        napi_default,             // 设置属性特性为默认
        NULL                      // 任何与属性相关的数据
    };

    // 在exports对象上定义属性
    napi_define_properties(env, exports, 1, &desc);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个例子中,我们创建了一个简单的本机插件,它暴露了一个函数myFunction。当你在 JavaScript 中调用这个函数时,它将返回字符串"Hello World"。

我们使用napi_define_properties函数来把myFunction添加到模块的导出对象上,并且我们设置了属性特性为napi_default,这意味着在 JavaScript 代码中,这个函数是可写的、可枚举的、可配置的。

如果我们想让这个函数成为只读的,我们就可以在napi_property_descriptor结构体中将napi_default改为napi_readonly

napi_property_descriptor desc = {
    "myFunction",
    NULL,
    MyFunction,
    NULL,
    NULL,
    NULL,
    napi_readonly,  // 设置属性特性为只读
    NULL
};

这样设置之后,在 JavaScript 代码中尝试修改myFunction属性将会失败。

napi_property_descriptoropen in new window

napi_property_descriptor 是 Node.js 中 N-API 的一部分,N-API 是一个用于构建原生插件的 API 层。原生插件是用 C 或者 C++ 写的模块,它们可以直接调用系统级别的 API,通常用于提高性能或者访问一些 JavaScript 没有直接支持的低层功能。

在介绍 napi_property_descriptor 之前,需要明白原生插件中经常需要和 JavaScript 对象交互。JavaScript 对象由属性组成,这些属性可以是值(如字符串、数字等),也可以是函数。napi_property_descriptor 就是用来定义一个属性及其特性的结构体(C 结构体),当你想要在原生代码中创建新的 JavaScript 对象或者给对象添加属性时会用到它。

napi_property_descriptor 包含以下字段:

  • utf8name: 这是一个指向字符数组的指针,代表属性的名字。
  • name: 一个 napi_value 类型,表示属性的名称,用于安全地处理可能包含非 ASCII 字符的属性名。
  • method: 如果这个属性是一个函数的话,method 是一个指向该函数原生实现的指针。
  • getter: 如果这个属性是通过 getter 函数获得的,getter 就是指向该 getter 函数的指针。
  • setter: 如果这个属性可以被设置,并且有 setter 函数,setter 就是指向该 setter 函数的指针。
  • value: 如果这个属性是一个简单的值,那么 value 就会包含这个值的 napi_value 表示。
  • attributes: 这是一个位掩码,用来定义属性的特性,比如是否可写 (napi_writable),是否可枚举 (napi_enumerable),是否可配置 (napi_configurable) 等。
  • data: 这是一个指向任意数据的指针,通常用于与属性相关联的数据。

现在,我们通过一个简单的例子来看看如何使用 napi_property_descriptor

假设我们正在编写一个原生插件,我们希望暴露一个 JavaScript 对象给 Node.js,这个对象有一个叫做 version 的只读属性和一个叫做 sayHello 的方法。

##include `<`node_api.h>

// 这个函数将会成为 sayHello 方法的原生实现
napi_value SayHelloMethod(napi_env env, napi_callback_info info) {
    // 创建一个 JS 字符串 "Hello, world!"
    napi_value greeting;
    napi_create_string_utf8(env, "Hello, world!", NAPI_AUTO_LENGTH, &greeting);

    return greeting; // 返回这个字符串
}

// Getter 函数用于获取 version 属性
napi_value GetVersionValue(napi_env env, napi_callback_info info) {
    napi_value version_value;
    napi_create_double(env, 1.0, &version_value); // 假设版本号是 1.0

    return version_value; // 返回版本号
}

// 初始化函数,这个函数会在模块加载时被调用
napi_value Init(napi_env env, napi_value exports) {
    // 定义 sayHello 属性
    napi_property_descriptor say_hello_desc = {
        .utf8name = "sayHello",
        .method = SayHelloMethod,
        .attributes = napi_default
    };

    // 定义 version 属性
    napi_property_descriptor version_desc = {
        .utf8name = "version",
        .getter = GetVersionValue,
        .attributes = napi_default | napi_read_only
    };

    // 给 exports 对象添加 sayHello 和 version 属性
    napi_define_properties(env, exports, 1, &say_hello_desc);
    napi_define_properties(env, exports, 1, &version_desc);

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的代码中,我们创建了两个 napi_property_descriptorsay_hello_descversion_desc,然后分别通过 napi_define_properties 把它们添加到导出的对象上。当 JavaScript 代码 require 这个原生模块时,就可以直接调用 sayHello() 方法和访问 version 属性了。

这就是 napi_property_descriptor 在 Node.js N-API 中的基本用法。通过这种方式,你可以构建丰富的原生插件,并把它们无缝集成到 Node.js 应用程序中。

Functionsopen in new window

Node.js 中的 N-API(Node.js API)是一个用 C 语言编写的接口,它允许你创建可以直接与 Node.js 的运行时交互的本地插件。这意味着你可以用像 C 或 C++这样的低级语言编写性能敏感的代码,并且可以在 Node.js 中作为模块调用。

在 Node.js v21.7.1 版本的文档中,“Functions”部分指的是 N-API 提供的一系列函数,这些函数允许开发者在本地插件中执行各种操作,比如创建对象、调用 JavaScript 函数、处理异常等。

我会用几个例子来解释这一点:

1. 创建原生对象

假设你想要在本地插件中创建一个新的 JavaScript 对象,并将其返回给 Node.js 的环境。

##include `<`node_api.h>

napi_value CreateObject(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value myObject;

  // 创建一个空对象。
  status = napi_create_object(env, &myObject);
  if (status != napi_ok) {
    // 处理错误...
  }

  return myObject;
}

// 注册函数,使其可以从Node.js调用。
NAPI_MODULE_INIT() {
  napi_status status;
  napi_value fn;

  // 创建一个新函数。
  status = napi_create_function(env, NULL, 0, CreateObject, NULL, &fn);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 设置导出的函数为 "CreateObject"
  status = napi_set_named_property(env, exports, "CreateObject", fn);
  if (status != napi_ok) {
    // 处理错误...
  }

  return exports;
}

2. 调用 JavaScript 函数

如果你的本地插件接收到了一个 JavaScript 函数作为参数,并希望在本地代码中调用它。

##include `<`node_api.h>

napi_value CallJsFunction(napi_env env, napi_callback_info info) {
  napi_status status;
  size_t argc = 1;
  napi_value args[1];
  napi_value global, jsFunction, result;

  // 获取JavaScript传递的参数。
  status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 确保我们得到一个函数。
  napi_valuetype valuetype;
  status = napi_typeof(env, args[0], &valuetype);
  if (status != napi_ok || valuetype != napi_function) {
    // 抛出错误...
  }

  jsFunction = args[0];

  // 获取全局对象。
  status = napi_get_global(env, &global);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 调用JavaScript函数,没有参数和返回值。
  status = napi_call_function(env, global, jsFunction, 0, NULL, &result);
  if (status != napi_ok) {
    // 处理错误...
  }

  return result;
}

这些示例展示了如何使用 N-API 提供的函数来创建对象和调用函数。当然,N-API 提供的功能远不止这些,但是上面提供了一个基本的了解。

这些概念可能对于编程新手来说有点复杂,因为它们涉及了跨越 JavaScript 和 C/C++两种不同编程语言界限的编程理念。如果你刚开始学习编程,并且专注于 JavaScript,你可能不需要立即深入了解 N-API。这更多是为需要高性能计算和对 Node.js 运行时有特别需求的高级用户提供的功能。

napi_get_property_namesopen in new window

napi_get_property_names 是 Node.js 中的一个 N-API 函数,这是一个用于创建本地插件的 API。简单来说,N-API 允许你用 C 或者 C++编写代码,这些代码可以直接与 Node.js 的 JavaScript 引擎 V8 交互。

现在来解释一下napi_get_property_names的作用。当你在 JavaScript 对象上使用这个函数时,它会返回那个 JavaScript 对象的所有可枚举属性名称的列表。所谓“可枚举”的属性,指的是那些可以通过常规循环如for...in来访问的属性。

在 C 或 C++中使用napi_get_property_names的伪代码大致如下:

napi_status status;
napi_value object; // 假设这是你想要获取其属性的JS对象
napi_value property_names_array;

// 调用napi_get_property_names来获取属性数组
status = napi_get_property_names(env, object, &property_names_array);

// 检查是否调用成功
if (status == napi_ok) {
  // 使用property_names_array
} else {
  // 处理错误情况
}

实际应用例子:

假设你正在编写一个 Node.js 本地插件,你需要分析一个从 JavaScript 传来的配置对象,这个对象可能是这样的:

{
  host: 'localhost',
  port: 8080,
  username: 'admin',
  password: 'passwd'
}

你想在你的 C/C++插件中找出所有的配置项。你可以使用napi_get_property_names这样做:

  1. 当 JavaScript 调用你的本地插件时,它会传递上述配置对象。
  2. 在你的插件代码中,你使用napi_get_property_names获取这个对象的所有属性名称。
  3. 然后,你可以遍历返回的属性名称数组,并对每个属性进行操作,例如读取值、进行校验等。

这种方式可以帮助你的 C/C++代码更方便地了解和操作 JavaScript 传入的对象,使得本地插件能够更紧密地与 JavaScript 代码集成。

napi_get_all_property_namesopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,它允许你在服务器端运行 JavaScript 代码。N-API 是 Node.js 的一个 C API 层,它提供了构建原生插件的接口,让开发者可以不用直接使用底层的 V8 API。

napi_get_all_property_names 是 N-API 中的一个函数,用于获取 JavaScript 对象上的所有属性名称(包括自身的和继承的属性)。这个函数是为了使得原生模块的开发者能够从 C/C++ 代码中检索出 JavaScript 对象的属性名列表。

函数签名

函数的签名如下:

napi_status napi_get_all_property_names(
    napi_env env,
    napi_value object,
    napi_key_collection_mode key_mode,
    napi_key_filter key_filter,
    napi_key_conversion key_conversion,
    napi_value* result);
  • env: 当前调用的环境。
  • object: 要检索属性的 JavaScript 对象。
  • key_mode: 指定要返回哪些类型的键,例如只返回自有属性或还包括原型链上的属性。
  • key_filter: 用于过滤键的条件,例如是否排除不可枚举的属性。
  • key_conversion: 键的转换方式,比如返回字符串形式还是符号。
  • result: 返回找到的属性名称数组。

参数解释

key_mode, key_filter, 和 key_conversion 是特殊的枚举值,分别用来指明:

  • key_mode: 应该检索对象自己的属性 (napi_key_own), 还是连同原型链上的属性一起 (napi_key_include_prototypes).
  • key_filter: 定义了哪些属性将被包括在内 —— 可枚举的(napi_key_enumerable)、可配置的(napi_key_configurable)、可写的(napi_key_writable)等。
  • key_conversion: 通常设置为 napi_key_keep_numbers 表示保持数字属性作为数字类型,而不是转换成字符串。

实际应用示例

假设你正在编写一个原生的 Node.js 模块,并希望检索一个给定 JavaScript 对象的所有可枚举属性名称。下面是如何使用 napi_get_all_property_names 来完成这项任务的伪代码例子:

##include `<`node_api.h>

// 假设你已经有了一个 napi_env env 和一个要检索属性的 napi_value jsObject.

napi_value propertyNames;

// 获取所有自有的、可枚举的属性名称。
napi_status status = napi_get_all_property_names(
    env,
    jsObject,
    napi_key_own,             // 只考虑对象自身的属性
    napi_key_enumerable_only, // 只选取可枚举的属性
    napi_key_keep_strings,    // 键作为字符串返回
    &propertyNames            // 这是输出参数,将返回属性名数组
);

if (status == napi_ok) {
    // 如果调用成功,你现在可以处理 propertyNames 数组了。
    // 例如,你可以遍历这些属性名并做进一步处理。
} else {
    // 处理错误情况
}

此段代码展示了如何通过 napi_get_all_property_names 获取一个 JS 对象自身所有可枚举属性的名称。需要注意的是,实际代码中应当添加更多的错误检查和处理逻辑。

请记住,这个函数主要在编写涉及与 JavaScript 对象交互的原生扩展时使用,并不常见于日常的 Node.js 应用程序开发中。大多数情况下,你会直接使用 JavaScript 本身提供的反射 API,例如 Object.keys()Object.getOwnPropertyNames() 等。

napi_set_propertyopen in new window

当然,很高兴帮助您理解 Node.js 中的napi_set_property这个函数。

首先,我们需要了解 N-API 是什么。N-API 是 Node.js 提供的一个 C 语言接口,它允许你用 C 或 C++编写插件(称为本机模块),这些插件可以直接与 Node.js 运行时环境交互。使用 N-API 编写的本机模块不受 Node.js 版本更迭的影响,意味着它们有很好的兼容性和稳定性。

napi_set_property是 N-API 中的一个函数,它用于将一个 JavaScript 值设置为对象的属性。这个函数在 C/C++代码中操作 JavaScript 对象时非常有用,尤其是当你创建或修改 JavaScript 对象,并想把它传回给 JavaScript 代码时。

使用napi_set_property之前,你需要掌握以下概念:

  • napi_env: 代表 N-API 环境的上下文,几乎所有 N-API 函数都会用到它。
  • napi_value: 表示 JavaScript 值的抽象表示,可以是数字、字符串、对象等。

现在,让我们看一下napi_set_property的函数签名:

napi_status napi_set_property(napi_env env,
                              napi_value object,
                              napi_value key,
                              napi_value value);
  • env: N-API 的执行环境。
  • object: 你要设置属性的 JavaScript 对象。
  • key: JavaScript 对象的属性名称,通常是一个字符串(也可以是 Symbol)。
  • value: 你想要设置的值。

举例来说,假设你用 C++写了一个函数,你想要创建一个 JavaScript 对象,然后添加一个属性name,其值为"Node.js"

##include `<`node_api.h>

// 假设你已经有了一个napi_env env变量

void CreateObjectWithProperty() {
    // 创建一个新的空对象
    napi_value js_object;
    napi_create_object(env, &js_object);

    // 创建字符串'Node.js'
    napi_value js_string;
    napi_create_string_utf8(env, "Node.js", NAPI_AUTO_LENGTH, &js_string);

    // 创建一个表示属性键的字符串'name'
    napi_value js_key;
    napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &js_key);

    // 使用napi_set_property将'name'属性设置到对象上
    napi_set_property(env, js_object, js_key, js_string);

    // js_object 现在是一个拥有 { name: 'Node.js' } 的JavaScript对象
}

在上面的代码中,我们首先创建了一个空的 JavaScript 对象js_object,然后创建了一个表示字符串"Node.js"napi_value。接着,我们创建了一个表示属性键"name"napi_value。最后,我们调用napi_set_property将这个属性添加到了对象上。

现在,如果这个 C++函数被绑定并从 JavaScript 代码中调用,JavaScript 代码就能接收到一个包含{ name: 'Node.js' }的对象。

注意,所有 N-API 函数都返回一个napi_status枚举值,这个值表示操作是否成功。在实际使用中,你应该检查每次 N-API 调用的返回值,确保没有错误发生。

napi_get_propertyopen in new window

Node.js 是一个基于 Chrome 的 V8 JavaScript 引擎运行的 JavaScript 环境。它让你可以用 JavaScript 编写服务器端代码,以及在非浏览器环境下运行 JavaScript。N-API 是 Node.js 提供的一个 API 层,这个 API 层的目标是为了减少原生模块编写者与 Node.js 引擎之间的耦合,并且跨不同版本的 Node.js 提供兼容性。

napi_get_property 是 N-API 中的一个函数,它允许原生模块代码(通常是用 C 或 C++ 写的)访问 JavaScript 对象的属性。当你想从原生代码中读取 JavaScript 对象上的某一个属性时,你可以使用这个函数。

下面我将通过一个简单的例子来说明 napi_get_property 的用法:

假设你有一个 JavaScript 对象如下所示:

let user = {
  name: "Alice",
  age: 25,
};

如果你正在编写一个原生插件,并且你想访问这个 user 对象的 name 属性,你会需要通过 N-API 调用 napi_get_property 函数。

C/C++ 原生代码的大致示例如下:

##include `<`node_api.h>

// ... 假设你已有 napi_env env 和 napi_value object 参数,其中 object 指向了我们的 user 对象

napi_status status;
napi_value name_key, name_value;
napi_value result;

// 首先创建一个表示属性名("name" 字符串)的 napi_value
status = napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &name_key);
// 检查是否成功创建字符串
if (status != napi_ok) {
  // 处理错误
}

// 然后使用 napi_get_property 获取属性值
status = napi_get_property(env, object, name_key, &name_value);
// 检查操作是否成功
if (status != napi_ok) {
  // 处理错误
}

// 如果操作成功,现在 name_value 将包含 user 对象的 name 属性值
// 你可以继续使用其他 N-API 函数来处理这个值,比如转换成 C 字符串等

// ...

上面的代码展示了如何在 N-API 中创建一个指向属性名的 napi_value(这里是 "name"),然后如何使用 napi_get_property 来获取对象上该属性的值。

请注意,实际上在编写原生扩展时,你会需要处理各种错误情况,诸如检查 napi_status 返回值确保每步操作都成功执行,并且可能需要与 JavaScript 代码更紧密地交互,比如接收参数或返回结果给 JavaScript 端。不过上述例子涵盖了 napi_get_property 的核心用法。

napi_has_propertyopen in new window

napi_has_property 是 Node.js 中的一个 N-API(Node.js API)函数,用来检查 JavaScript 对象是否拥有某个属性。N-API 是一套 C 语言的 API,它允许你使用 C 或 C++ 来编写可从 JavaScript 代码中调用的原生扩展模块。

为了更好地理解这个函数,我们首先要了解几个基本概念:

  1. JavaScript 对象:在 JavaScript 中,对象是键值对的集合。例如,{ name: "Alice", age: 25 } 是一个包含两个属性 nameage 的对象。

  2. 属性:对象中的每个键(如 nameage)被称为属性。

  3. 原生扩展:这是直接用 C/C++ 编写的代码,它进行与 JavaScript 核心引擎的交互,通常用来执行不能直接用 JavaScript 完成或者性能较差的任务。

现在,让我们看看 napi_has_property 函数的定义:

napi_status napi_has_property(napi_env env,
                              napi_value object,
                              napi_value key,
                              bool* result);
  • env: 这是一个代表 N-API 环境的句柄。它是大多数 N-API 函数的第一个参数,用于管理和维护与 Node.js 引擎的交互。
  • object: 这是要检查属性的 JavaScript 对象。
  • key: 这是要检查的属性名称。它也是一个 JavaScript 值,通常是一个字符串或符号(Symbol)。
  • result: 这是一个指向布尔值的指针,函数会将结果存入这个地址。如果属性存在,那么这个值会设置为 true;否则,为 false

napi_has_property 的返回值是 napi_status 类型,其中包含了操作的状态信息,例如是否成功执行或遇到了错误。

实际运用例子

假设我们正在编写一个原生扩展,需要检查传入的 JavaScript 对象是否具有 username 属性。以下是一段可能的 C/C++ 代码示例:

##include `<`node_api.h>

// ...其他必要的代码...

napi_value CheckProperty(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void* data;

    // 获取函数参数
    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    // 检查是否提供了足够的参数
    if (argc `<` 1) {
        napi_throw_type_error(env, NULL, "Wrong number of arguments");
        return NULL;
    }

    // 创建一个表示属性名 "username" 的 JavaScript 字符串
    napi_value key;
    napi_create_string_utf8(env, "username", NAPI_AUTO_LENGTH, &key);

    // 检查对象是否含有这个属性
    bool hasProperty;
    napi_has_property(env, args[0], key, &hasProperty);

    // 根据结果返回 JavaScript 值 true 或 false
    napi_value result;
    napi_get_boolean(env, hasProperty, &result);

    return result;
}

// ...其他必要的代码...

在这个例子中,我们定义了一个名为 CheckProperty 的函数,它检查传递给它的第一个参数(一个 JavaScript 对象)是否有 username 属性,并返回一个表示结果的布尔值。

请注意,为了实际在 Node.js 中使用这个原生扩展,你还需要编写额外的代码来注册扩展模块、方法和初始化它们。以上只是关于 napi_has_property 如何使用的简单演示。

napi_delete_propertyopen in new window

当然,让我们来聊一聊 Node.js 中的 napi_delete_property 函数。首先,我们需要了解 N-API 是什么:N-API 是一个 C 语言的 API,它是 Node.js 提供的一个稳定的抽象层,允许你编写可跨不同版本 Node.js 运行的本地插件。

现在,让我们深入到 napi_delete_property 这个函数:

napi_delete_property

这个函数用于从一个 JavaScript 对象中删除一个属性。它是 N-API 的一部分,并且接受几个参数来完成操作。

参数:

  • env: 这是表示 N-API 环境的一个句柄,它提供了大量的 N-API 调用所需的上下文信息。
  • object: 这是你想要删除属性的那个 JavaScript 对象。
  • key: 这代表了要被删除的属性的名称或者 Symbol。

返回值:

  • 如果操作成功,它将返回 napi_ok,这表明属性已经被成功删除。
  • 如果有错误发生,它将返回一个代表相应错误类型的枚举值。

实际运用例子:

假设你正在编写一个 Node.js 的本地插件,而你希望删除一个对象中的某个属性。假设我们有一个 JavaScript 对象如下:

let myObject = {
  a: 1,
  b: 2,
  c: 3,
};

现在,你想在你的本地插件中删除属性 b

使用 napi_delete_property 的 C 代码可能看起来像这样:

##include `<`node_api.h>

napi_value DeletePropertyExample(napi_env env, napi_callback_info info) {
    napi_status status;
    size_t argc = 1;
    napi_value args[1];
    napi_value object_to_delete_property_from;

    // 获取传递给函数的 JavaScript 对象
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
    if (status != napi_ok) {
        // 处理错误...
    }

    object_to_delete_property_from = args[0]; // 第一个参数是我们的对象

    // 创建一个表示属性 'b' 的字符串
    napi_value key;
    status = napi_create_string_utf8(env, "b", NAPI_AUTO_LENGTH, &key);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 删除属性 'b'
    bool result;
    status = napi_delete_property(env, object_to_delete_property_from, key, &result);
    if (status != napi_ok) {
        // 处理错误...
    }

    if (result) {
        // 属性 'b' 已被删除
    } else {
        // 属性 'b' 没有被删除(可能该属性不存在或不可配置)
    }

    // 返回undefined,因为这个函数不需要返回任何值
    napi_value undef;
    status = napi_get_undefined(env, &undef);
    return undef;
}

// 注册这个函数以便在JavaScript中可以调用
NAPI_MODULE_INIT() {
    napi_value deleteFn;
    napi_create_function(env, NULL, 0, DeletePropertyExample, NULL, &deleteFn);
    napi_set_named_property(env, exports, "deletePropertyExample", deleteFn);
    return exports;
}

在 JavaScript 里,你可以这样调用这个本地函数:

const nativeAddon = require("path-to-your-native-addon.node");
nativeAddon.deletePropertyExample(myObject);

console.log(myObject); // { a: 1, c: 3 }, 属性 'b' 已经被删除

以上就是 napi_delete_property 的基础介绍和一个简单的使用示例。希望能帮助你理解这个函数如何在实际中应用!

napi_has_own_propertyopen in new window

napi_has_own_property 是 Node.js 中的一个函数,这个函数是 Node.js 的 N-API(Node.js API)的一部分。N-API 是一个 C 层面的 API,让你可以创建原生的插件,这些插件可以直接和 Node.js 交互,通常用来提升性能或者让 Node.js 能够使用那些只有 C/C++库才有的功能。

在解释 napi_has_own_property 前,让我们先理解一下 JavaScript 中的对象属性。在 JavaScript 中,每个对象都有一些属性,它们可以是直接定义在该对象上的(也就是"自有属性"),也可以是从它的原型链中继承而来的。检查一个属性是否为对象的自有属性很重要,因为这关系到了属性的枚举、遍历和权限控制等问题。

现在回到 napi_has_own_property 函数。这个函数的作用是告诉我们一个给定的属性是否是 JavaScript 对象的自有属性。它是 N-API 的一部分,因此在 C 或 C++中编写的代码中被使用,不是在常规的 JavaScript 代码中。

函数原型:

napi_status napi_has_own_property(napi_env env,
                                  napi_value object,
                                  napi_value key,
                                  bool* result);

参数解释:

  • env: 这是表示 N-API 调用的上下文的环境句柄。
  • object: 这是我们要检查属性的 JavaScript 对象。
  • key: 表示我们想要检查的属性的名称或者 Symbol。
  • result: 指向布尔值的指针,函数执行后会将结果存储在这个布尔值中。如果属性确实存在并且是自有属性,则结果为 true;否则为 false。

函数返回值:

  • 返回一个类型为 napi_status 的值,这个值表示函数调用是否成功。如果成功,返回 napi_ok,否则返回错误码。

使用场景例子:

假设你创建了一个 Node.js 原生模块,需要对传入的 JavaScript 对象进行操作,并且你需要判断这个对象是否具有特定的属性,而且这个属性必须是该对象自己独有的,不是从原型继承来的。

C 代码示例:

##include `<`node_api.h>

// ...

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_status status;

    // 获取JavaScript传递的参数
    size_t argc = 1;
    napi_value args[1];
    status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 校验参数并执行相关操作
    if (status == napi_ok && argc == 1) {
        napi_value objectToCheck = args[0];

        // 我们要检查的属性
        napi_value key;
        status = napi_create_string_utf8(env, "myProperty", NAPI_AUTO_LENGTH, &key);

        // 检查属性
        bool hasOwnProperty;
        status = napi_has_own_property(env, objectToCheck, key, &hasOwnProperty);

        // 根据结果进行处理
        if (status == napi_ok && hasOwnProperty) {
            // 如果有这个自有属性,执行一些操作...
        } else {
            // 如果没有这个属性或者出错了,执行其他操作...
        }
    }

    // ...

    // 返回一些值给JavaScript
    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

// ...

在这个例子中,我们首先获取了通过 JavaScript 传递过来的参数,然后创建了一个字符串键来表示我们要检查的属性 "myProperty"。使用 napi_has_own_property() 函数检查这个属性是否为对象的自有属性,最后根据结果执行相应的操作。

总结起来,napi_has_own_property 在 Node.js 的 N-API 中用于原生模块开发,它帮助我们检查一个属性是否是 JavaScript 对象的自有属性。这对于进行有效的属性管理和正确的内存操作非常重要,尤其是在涉及原生资源时。

napi_set_named_propertyopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行时环境。它让我们能够使用 JavaScript 来编写服务器端的代码。N-API 则是 Node.js 提供的一个用于构建原生插件的 API。

在 Node.js 中,你可以用 JavaScript 写大部分的功能,但有时候你可能需要直接使用 C 或者 C++来写一些性能关键型的代码或者与操作系统交互。这些用 C 或 C++写的模块称为“原生插件”。

napi_set_named_property函数就是这样一个 N-API 的函数,它允许原生插件(C/C++代码)给一个 JavaScript 对象设置属性。

这个函数原型如下:

napi_status napi_set_named_property(napi_env env,
                                    napi_value object,
                                    const char* utf8name,
                                    napi_value value);

参数解释:

  • env:代表当前的 N-API 环境上下文。
  • object:是要添加属性的 JavaScript 对象。
  • utf8name:是一个以 null 结尾的字符串,表示你想要设置的属性的名称。
  • value:是一个napi_value,表示属性的值,它可以代表任何 JavaScript 数据类型,例如一个数字、字符串、对象等。

返回值:napi_status,它表示操作成功还是失败等状态信息。

现在举个例子,假设你正在写一个原生插件,你想要创建一个 JavaScript 对象,并且给它添加一个属性叫version,其值为字符串"1.0.0"。下面是你可能会怎么做的 C 代码示例:

##include `<`node_api.h>

// 这个函数被暴露给JavaScript,因此可从Node.js中调用
napi_value CreateObject(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_value myObject;

    // 创建一个新的空对象
    status = napi_create_object(env, &myObject);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 创建一个JavaScript字符串,作为属性的值
    napi_value versionString;
    status = napi_create_string_utf8(env, "1.0.0", NAPI_AUTO_LENGTH, &versionString);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 设置对象的"version"属性
    status = napi_set_named_property(env, myObject, "version", versionString);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 返回创建的对象
    return myObject;
}

// 模块初始化函数,此处将CreateObject关联到名为"createObject"的导出项
NAPI_MODULE_INIT() {
    napi_value exportFunc;
    napi_status status = napi_create_function(env, NULL, 0, CreateObject, NULL, &exportFunc);
    if (status != napi_ok) {
        // 处理错误...
    }
    return exportFunc;
}

当从 Node.js 代码中调用这个模块的createObject方法时,它就会返回我们刚刚创建并设置属性的对象。

在 JavaScript 中,你可能会这样使用它:

const addon = require("./build/Release/addon");

const obj = addon.createObject();
console.log(obj.version); // 将输出: 1.0.0

通过这种方式,你就能在 C/C++中构建复杂的数据结构,然后把它们传递给 Node.js 的 JavaScript 环境,非常方便地进行进一步处理。

napi_get_named_propertyopen in new window

当然,我很乐意帮助你理解这个概念。

Node.js 的 N-API 是一个 API,它允许你编写本地插件。所谓本地插件,就是用 C 或者 C++ 写的代码,这些代码可以直接调用 Node.js 的功能、或者反过来被 Node.js 调用。这样做的好处是可以利用 C/C++ 的性能优势,或者使用已经存在的 C/C++ 库。

napi_get_named_property 这个函数是 N-API 的一部分,用于从 JavaScript 对象中获取属性的值。简单来说,就是如果你有一个 JavaScript 对象,而你又在 C/C++ 代码中工作,你可以使用这个函数来读取 JavaScript 对象的属性。

比如说,如果我们有一个这样的 JavaScript 对象:

let person = {
  name: "Alice",
  age: 25,
};

如果我们想在 C/C++ 插件中获取这个对象的 name 属性的值,我们就可以使用 napi_get_named_property 函数。

下面是一个例子,展示了如何在 C/C++ 中使用这个函数:

##include `<`node_api.h>

// 假设 env 是 napi_env 类型的变量,obj 是 napi_value 类型的变量,
// 表示上面提到的 person 对象

napi_value name_value;
napi_status status = napi_get_named_property(env, obj, "name", &name_value);

if (status == napi_ok) {
  // 如果成功,name_value 现在包含了 name 属性的值
  // 然后你可以把它转换成 C/C++ 字符串或其他你需要的格式
}

在这段代码中,env 是一个表示当前环境的变量,所有的 N-API 调用都需要它。obj 是一个 napi_value 类型的变量,表示我们要操作的 JavaScript 对象。"name" 是我们想要读取的属性名,name_value 将会得到属性的值(也是一个 napi_value 类型,因为在 N-API 中,JavaScript 值都由这个类型表示)。

但是要注意的是,这个函数只获取属性的值,并不执行任何 JavaScript 代码。这意味着如果属性是 getter 函数,它不会被调用。

这种机制在与 JavaScript 交互时非常有用,特别是当你需要高性能处理或者需要将现有的 C/C++ 代码集成到 Node.js 应用程序中时。

napi_has_named_propertyopen in new window

napi_has_named_property 是一个函数,它来自 Node.js 的 N-API(Node.js API),这是一个用于构建原生插件的接口。N-API 提供了一系列的 C 语言 API,允许本地代码与 JavaScript 代码进行交互。在这种情况下,napi_has_named_property 允许你检查一个 JavaScript 对象是否有一个特定名称的属性。

使用场景解释:

当你在编写一个原生模块(通常是用 C 或者 C++ 写的)时,你可能需要检查一个从 JavaScript 传递过来的对象是否拥有某个属性。例如,如果你正在编写一个处理配置对象的模块,那么你可能想要检查用户是否提供了某些必要的设置。

函数签名:

这个函数的声明如下所示:

napi_status napi_has_named_property(napi_env env, napi_value object, const char* utf8name, bool* result);
  • env: 表示一个 N-API 调用环境,它贯穿整个 N-API 的使用周期。
  • object: 是指向你想要检查属性的 JavaScript 对象的引用。
  • utf8name: 是一个字符串,表示你要检查的属性名。
  • result: 是一个布尔值的指针,在函数执行后,*result 会被设置为 true 如果该属性存在,否则为 false

返回值:

这个函数会返回一个 napi_status 值,告诉你调用是否成功。napi_status 是一个枚举,包含了多种状态,比如 napi_ok 表示操作成功完成。

实际例子:

假设我们在编写一个原生模块,这个模块需要用户传递一个包含 config 属性的对象:

JavaScript 侧代码可能是这样的:

const nativeModule = require("your-native-module");

nativeModule.process({
  config: {
    //... 配置信息
  },
});

在你的原生模块中,你可能需要这样判断 process 方法接收到的对象是否含有 config 属性:

##include `<`node_api.h>

// 假设 object 是从 JS 传过来的参数
napi_value object;
bool hasConfig;
napi_status status = napi_has_named_property(env, object, "config", &hasConfig);

if (status != napi_ok) {
  // 处理错误...
}

if (hasConfig) {
  // 继续处理存在 config 属性的逻辑...
} else {
  // 抛出异常或处理不存在 config 属性的情形...
}

上述代码展示了如何使用 napi_has_named_property 来确认一个对象是否具有 config 属性,并据此进行不同的处理策略。如果发现没有 config 属性,可以抛出一个错误,告诉 JS 代码缺少必要的配置。

napi_set_elementopen in new window

napi_set_element是 Node.js 中 N-API 的一部分,N-API 是一个用于构建原生插件的 API 层。简单来说,原生插件允许你用 C 或 C++编写代码,这些代码可以直接在 Node.js 运行时环境中执行,提供性能上的优势,特别是对于需要大量计算的任务。

napi_set_element具体是一个函数,它用于设置 JavaScript 数组中指定索引位置的元素值。当你用 C 或 C++编写与 Node.js 交互的代码时,你可能需要操作 JavaScript 对象和数组。这个函数就是用来完成数组操作的部分工作。

函数签名

napi_status napi_set_element(
    napi_env env,
    napi_value array,
    uint32_t index,
    napi_value value
);
  • env:表示当前的 N-API 环境,通常是你在每个 N-API 函数中都会见到的参数。任何使用 N-API 的函数几乎总是需要这个环境变量。
  • array:这是你想要修改的 JavaScript 数组的引用。
  • index:你希望设置的数组元素的索引(位置)。
  • value:这是你想要放置在数组中的那个索引处的值的引用。

实际例子

假设你有一个 JavaScript 数组,并且你想要在 C++扩展中更改第二个元素的值。在 Node.js 代码中,你可能会这样做:

let myArray = [1, 2, 3];
myArray[1] = 20; // 设置数组的第二个元素为20

但是在 C++的 N-API 中,你会使用napi_set_element来达到同样的目的:

##include `<`node_api.h>

napi_value SetElementExample(napi_env env, napi_callback_info info) {
    napi_status status;

    // 提前定义好你将用到的N-API变量
    napi_value myArray;
    napi_value valToSet;
    uint32_t index = 1; // 我们想要设置的索引

    // 创建一个新的JavaScript数组
    status = napi_create_array(env, &myArray);
    if (status != napi_ok) return nullptr;

    // 让我们假设要设置的值是20
    status = napi_create_int32(env, 20, &valToSet);
    if (status != napi_ok) return nullptr;

    // 设置数组的第二个元素(index=1,因为索引是从0开始的)
    status = napi_set_element(env, myArray, index, valToSet);
    if (status != napi_ok) return nullptr;

    // 返回已被修改的数组
    return myArray;
}

...

// 然后你还需要注册这个函数,以便它可以从Node.js调用,
// 这通常在N-API模块的初始化函数中完成。

上面的例子演示了如何创建一个数组并设置其特定索引处的值。这只是napi_set_element的一个简单应用实例,在实际开发中,你可能需要处理更复杂的数据结构和错误检查流程。

napi_get_elementopen in new window

napi_get_element 是 Node.js 中的一个 N-API 函数,用于从 JavaScript 数组中获取指定索引处的值。N-API 是 Node.js 提供的一套 C API,使得本地插件(通常是 C 或 C++编写的模块)能够与 JavaScript 代码交互,并且不受 Node.js 版本变动的影响。

基本概念:

  • N-API:一组固定的 API,独立于底层的 JavaScript 运行时(如 V8),旨在保持二进制兼容性,即使 Node.js 的版本升级也不需要对本地插件重新编译。
  • JavaScript 数组:在 JavaScript 中,数组是一种用来存储多个值的单一变量。

napi_get_element函数的作用: 这个函数用于访问 JavaScript 中的数组元素。它的签名大致如下(使用 C 语言):

napi_status napi_get_element(napi_env env,
                             napi_value array,
                             uint32_t index,
                             napi_value* result);
  • env:表示 N-API 调用的上下文。
  • array:一个 napi_value,代表 JavaScript 中的数组。
  • index:要访问数组的哪个位置的元素,以 0 开始的索引值。
  • result:用来接收位于提供索引的数组元素的 napi_value

函数返回一个 napi_status 值,表示操作成功与否。

实际应用例子:

假设有如下 JavaScript 数组:

let numbers = [10, 20, 30, 40, 50];

你想通过一个 C/C++的 Node.js 插件获取这个数组的第三个元素(30)。你的 C 代码会像这样使用napi_get_element

##include `<`node_api.h>

// 这个函数被假定为已经设置好与JavaScript环境相联系
void GetThirdElementExample(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value this_arg;
    void* data;

    // 获取JavaScript传递给这个C函数的参数
    napi_get_cb_info(env, info, &argc, args, &this_arg, &data);

    // args[0] 应该是我们的JavaScript数组
    napi_value array = args[0];

    // 我们想要获取第三个元素,索引值为2
    uint32_t index = 2;
    napi_value thirdElementValue;

    // 调用napi_get_element获取数组的第三个元素
    napi_status status = napi_get_element(env, array, index, &thirdElementValue);

    // 确保操作成功
    if (status == napi_ok) {
        // 如果操作成功,thirdElementValue 现在就包含了数字30

        // 以下代码可以将napi_value转换为C类型的值,比如int
        int thirdElement;
        napi_get_value_int32(env, thirdElementValue, &thirdElement);

        // 现在你可以使用这个值做进一步的操作了
        // ...
    } else {
        // 错误处理:可以通过status来判断发生了什么错误
    }
}

这段代码演示了如何从 Node.js 传递到原生插件的数组中获取特定元素的值,并将其转换为 C 语言中的整数。通过这样的方式,你可以在 C/C++代码中实现复杂的逻辑处理,同时利用 JavaScript 进行高层次的应用程序开发。

napi_has_elementopen in new window

napi_has_element 是 Node.js 中 N-API (Native API) 的一部分,它提供了一种在原生模块中与 JavaScript 交互的稳定、跨版本的接口。

这个函数的作用是检查一个 JavaScript 对象是否具有给定的索引属性。在 JavaScript 中,你可以把对象当作数组来使用,访问它的元素通过索引(比如 obj[0], obj[1]),而 napi_has_element 就是用来判断指定索引的元素是否存在。

下面将详细解释 napi_has_element 的用法,并给出一个简单的例子:

函数原型:

napi_status napi_has_element(napi_env env,
                             napi_value object,
                             uint32_t index,
                             bool* result);
  • env: napi_env 对象,它代表了 Node.js 的运行环境,你可以通过这个环境来调用其他 N-API 函数。
  • object: napi_value 类型,表示一个 JavaScript 对象,我们将检查这个对象是否含有特定索引的元素。
  • index: 一个 uint32_t 整数,表示要检查的索引值。
  • result: 指向布尔类型的指针,函数会设置这个值以指示对象是否有该索引的元素。

返回值:

napi_has_element 返回 napi_status 类型的值,这表明操作是否成功。如果函数调用成功,返回 napi_ok;如果出现错误,则返回不同的错误码,表明发生了什么问题。

实际使用例子:

假设我们编写了一个原生模块,在这个模块中,我们想要检查某个 JavaScript 数组或者类似数组的对象是否有位于第二个位置的元素(索引为 1)。

##include `<`node_api.h>

// ... 定义其他必要的变量和初始化代码 ...

napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    napi_value args[1];
    size_t argc = 1;
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 假设 args[0] 是传入的 JavaScript 对象
    bool hasElement;
    napi_status status = napi_has_element(env, args[0], 1, &hasElement);

    if (status != napi_ok) {
        // 如果出现错误,处理错误逻辑
        // ...
    }

    // 根据查询结果执行不同的逻辑
    if (hasElement) {
        // 对象中有索引为 1 的元素
        // ...
    } else {
        // 对象中没有索引为 1 的元素
        // ...
    }

    // 返回一些值或 undefined
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

// 注册原生函数等其他相关代码...

在上面的例子中,我们定义了一个原生函数 MyNativeFunction,它接收一个参数并且调用 napi_has_element 来检查该参数是否有索引为 1 的元素。根据检查的结果,我们可以决定执行不同的逻辑。最后,这个函数返回了 undefined 或者其它一些值,取决于具体实现。

napi_delete_elementopen in new window

在 Node.js 中,napi_delete_element是一个函数,属于 N-API(Node API),这是一套用于构建本地插件的 API。N-API 设计的目的是为了让 C 或 C++编写的插件能够在不同版本的 Node.js 中运行而不需要重新编译,提高模块的稳定性和兼容性。

napi_delete_element函数的作用是从 JavaScript 对象中删除一个属性,与 JavaScript 中的delete object[property]操作相似。但这里的对象是一个数组,因此该函数特指从数组中移除索引处的元素。

我们现在来详细解释一下这个函数:

函数签名

napi_status napi_delete_element(napi_env env, napi_value object, uint32_t index, bool* result);
  • napi_env env: 这是表示 N-API 环境的句柄。它是一个上下文对象,提供了许多 N-API 调用所需的信息。
  • napi_value object: 这是数组对象的句柄,表示要操作的数组。
  • uint32_t index: 这是你想要删除的数组元素的索引。
  • bool* result: 这个参数会返回操作是否成功。如果删除成功,它会被设置为true;否则为false

返回值

返回一个napi_status枚举值,代表函数执行的状态。如果返回napi_ok,则表示没有错误发生。

举例说明

假设我们有一个 Node.js 的本地插件,在 C/C++代码中,我们希望删除一个 JavaScript 数组中的特定元素。我们的代码可能如下所示:

##include `<`node_api.h>

// 假设这个函数是为了从给定的JavaScript数组中删除特定索引处的元素

napi_value DeleteElementFromJSArray(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2];
    napi_value this_arg;
    void* data;

    // 解析传入的参数
    napi_get_cb_info(env, info, &argc, args, &this_arg, &data);

    // args[0] 应该是一个数组
    napi_value js_array = args[0];

    // args[1] 应该是一个数字,表示数组中要删除的元素索引
    uint32_t index;
    napi_get_value_uint32(env, args[1], &index);

    // 执行删除操作
    bool result;
    napi_status status = napi_delete_element(env, js_array, index, &result);

    // 检查操作是否成功
    if (status == napi_ok && result) {
        // 删除成功
        return nullptr; // 或返回一个表示成功的JavaScript值
    } else {
        // 删除失败,可以根据具体情况处理错误
        return nullptr; // 或返回一个表示失败的JavaScript值
    }
}

// 将上面的函数暴露为模块的导出
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, DeleteElementFromJSArray, NULL, &fn);
    napi_set_named_property(env, exports, "deleteElement", fn);
    return exports;
}

在这个例子中,我们定义了一个DeleteElementFromJSArray函数,它接收两个参数:第一个是 JavaScript 数组,第二个是要删除的元素索引。使用napi_get_cb_info获取 JavaScript 传递的参数,然后调用napi_delete_element执行删除操作。最后,根据操作结果返回相应的 JavaScript 值。

要注意的是,这只是一个简化的例子,实际上还需要进行更多的错误检查和边界检查等操作。

napi_define_propertiesopen in new window

napi_define_properties 是 Node.js 中 N-API 的一部分,N-API 是一个用于构建原生插件的 API。原生插件是用 C 或 C++编写的,可以直接调用 Node.js 底层 API,通常用于提升性能或实现 Node.js 本身无法直接实现的功能。

在介绍 napi_define_properties 之前,我们需要理解几个概念:

  1. N-API: Node.js 的 Native API,使得开发者能够不依赖于 V8 引擎的内部结构来构建扩展模块。这意味着使用 N-API 的扩展模块与 Node.js 的版本无关,可以在不同版本的 Node.js 中运行而无需重新编译。

  2. 对象属性: 在 JavaScript 中,对象由属性组成,每个属性都有一个键(key)和相应的值(value)。属性还可以包含如是否可写、是否可枚举等特性。

  3. napi_value: 这是一个代表 JavaScript 值的抽象句柄,在 N-API 函数中用来表示各种类型的值,比如数字、字符串、对象等。

  4. napi_property_descriptor: 这是一个结构体,用来描述对象的属性,包括属性名称、属性特性等。

现在,让我们看看 napi_define_properties 函数。它用于在一个已存在的 JavaScript 对象上定义新的属性。这个函数的原型如下:

napi_status napi_define_properties(napi_env env,
                                   napi_value object,
                                   size_t properties_count,
                                   const napi_property_descriptor* properties);

参数解释:

  • env: 当前执行环境。
  • object: 要定义属性的 JavaScript 对象。
  • properties_count: 要定义的属性数量。
  • properties: 指向napi_property_descriptor数组的指针,每个元素描述一个属性。

使用这个函数时,你首先创建一个或多个 napi_property_descriptor 结构体来表示你想在 JavaScript 对象上定义的属性,然后通过 napi_define_properties 将它们添加到对象上。

举个例子,假设你正在编写一个原生模块,需要在一个对象上定义两个属性:一个是可读写的 myNumber,另一个是只读的 myString

##include `<`node_api.h>

napi_value Init(napi_env env, napi_value exports) {
  // 定义属性描述符数组
  napi_property_descriptor properties[] = {
    { "myNumber", NULL, 0, 0, 0, 0, napi_default, 0 },
    { "myString", NULL, 0, 0, 0, 0, napi_default, (void*)"this is a string" }
  };

  // 创建一个新的空对象
  napi_value myObject;
  napi_create_object(env, &myObject);

  // 定义myNumber为一个可读写的属性,默认值是42
  napi_value numberValue;
  napi_create_double(env, 42, &numberValue);
  properties[0].value = numberValue;

  // 定义myString为一个只读的属性
  properties[1].getter = [](napi_env env, napi_callback_info info) -> napi_value {
    void* data;
    napi_get_cb_info(env, info, NULL, NULL, NULL, &data);
    napi_value stringValue;
    napi_create_string_utf8(env, static_cast`<`char*>(data), NAPI_AUTO_LENGTH, &stringValue);
    return stringValue;
  };

  // 将属性添加到对象上
  napi_define_properties(env, myObject, sizeof(properties) / sizeof(properties[0]), properties);

  // 导出这个对象
  napi_set_named_property(env, exports, "myModule", myObject);

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这段代码中,我们创建了一个新的 JavaScript 对象,并为它定义了两个属性。myNumber 属性被设置为初始值 42,并且是可读写的。myString 属性通过一个 getter 函数定义,当在 JavaScript 中访问这个属性时,getter 函数会被调用,返回一个字符串"this is a string"。

最后,我们将这个对象导出为模块的一部分,这样在 JavaScript 代码中就可以通过 require 加载并使用这个对象及其定义的属性了。

napi_object_freezeopen in new window

Node.js 是一个十分流行的 JavaScript 运行环境,它允许你在服务器端运行 JavaScript 代码。N-API 是 Node.js 提供的一套用于构建原生插件的 API。原生插件是使用像 C 或者 C++这样的语言编写的模块,可以直接和 Node.js 的底层 API 进行交互。

napi_object_freeze 是 N-API 中的一个函数,其目的在于让一个 JavaScript 对象变得不可修改,也就是说,一旦一个对象被冻结(freeze),就不能再向它添加新的属性,也不能删除或修改已有的属性,甚至现有的属性也不能被重新配置(比如改变它们的可枚举性、可配置性等)。这个过程类似于 JavaScript 中 Object.freeze() 方法的功能。

这里是 napi_object_freeze 函数的一般用法:

napi_status napi_object_freeze(napi_env env,
                               napi_value object);

参数解释:

  • env: 这是表示当前环境的句柄,它是所有 N-API 调用的上下文。
  • object: 这是你想要冻结的 JavaScript 对象。

返回值是 napi_status 类型,它会告诉你操作是否成功完成。

现在让我们通过一个简单的例子来理解如何使用 napi_object_freeze:

假设你正在编写一个 Node.js 原生扩展,你希望提供一个对象给 JavaScript 侧,但你不想让用户能够更改这个对象。

首先,你需要创建一个 JavaScript 对象,并将其传递给 napi_object_freeze 函数进行冻结。以下是一个使用 C 语言编写的示例代码片段:

##include `<`node_api.h>

// ...其他必要的代码...

napi_value CreateFrozenObject(napi_env env) {
    napi_status status;
    napi_value my_object;

    // 创建一个新的空对象
    status = napi_create_object(env, &my_object);
    if (status != napi_ok) return nullptr;

    // 在这里你可能会给对象添加一些初始属性

    // 冻结对象以防止更改
    status = napi_object_freeze(env, my_object);
    if (status != napi_ok) return nullptr;

    // 返回冻结后的对象
    return my_object;
}

// ...更多代码...

在你的原生模块中调用 CreateFrozenObject 函数就会返回一个被冻结的对象,JavaScript 代码尝试去修改这个对象将会失败,并可能抛出异常。

记住,当你在实际开发中使用 napi_object_freeze 时,总是要检查返回状态确保操作成功。如果 napi_object_freeze 调用失败了,你应该妥善处理错误情况。

napi_object_sealopen in new window

napi_object_seal 是 Node.js 中的 N-API(原生 API)的一部分,它提供了一个与 JavaScript V8 引擎的直接交互层。这个函数用于使一个 JavaScript 对象变为“密封”的(sealed),也就是说,在对象被密封之后,你将不能再向这个对象添加新的属性,已有的属性也不能被删除,但是你仍然可以修改已有属性的值(只要这些属性不是可写的)。

在现实中,密封一个对象可能在你想固化某个对象的结构时非常有用,确保没有其他代码可以给对象添加或移除属性,这样可以提供更稳定的接口。

现在让我们以一个简单的例子来说明napi_object_seal如何工作:

假设你正在开发一个 Node.js 的原生插件,你需要创建一个 JavaScript 对象并且不允许用户对这个对象进行修改。

首先,你需要包含 N-API 的头文件,并定义你的原生函数,比如SealObject

##include `<`node_api.h>

// 假设这是你的N-API初始化方法内部的一个函数实现
napi_value SealObject(napi_env env, napi_callback_info info) {
  napi_status status;
  napi_value object;

  // 创建一个新的空对象
  status = napi_create_object(env, &object);
  if (status != napi_ok) return nullptr;

  // 给对象设置一些属性
  napi_value value;
  status = napi_create_string_utf8(env, "value", NAPI_AUTO_LENGTH, &value);
  if (status != napi_ok) return nullptr;
  status = napi_set_named_property(env, object, "key", value);
  if (status != napi_ok) return nullptr;

  // 密封这个对象
  status = napi_object_seal(env, object);
  if (status != napi_ok) return nullptr;

  // 返回这个密封的对象
  return object;
}

在这个例子中,我们首先创建了一个新的 JavaScript 对象,然后给它设置了名为"key"的属性,其对应的值为"value"。然后通过napi_object_seal函数,我们将这个对象密封起来,这样在返回给 JavaScript 后,就无法再给这个对象添加新的属性了。

在你的 JavaScript 代码中,当你调用这个原生模块中的SealObject方法时,它会返回一个密封的对象。如果你尝试给这个对象添加新的属性,JavaScript 运行时将会抛出错误。

const myNativeModule = require("my-native-module");
const sealedObject = myNativeModule.SealObject();
console.log(sealedObject.key); // 输出: value

// 尝试添加新的属性,将失败
sealedObject.newKey = "new value"; // 抛出错误

// 修改现有的属性值是可以的
sealedObject.key = "new value"; // 这是有效的,前提是该属性是可写的

// 尝试删除属性,将失败
delete sealedObject.key; // 抛出错误

请注意,这是一个原生插件的示例,通常,你需要编译 C/C++代码成为.node 文件,然后才能在 Node.js 环境中使用它。而使用napi_object_seal属于较高级的操作,对于编程新手而言,理解 JavaScript 层面的对象操作可能会更加直接和容易。

Working with JavaScript functionsopen in new window

Node.js 中的 N-API 是一个 C 语言的 API,它允许你编写能够与 JavaScript 代码交互的本地插件。在 N-API 中“Working with JavaScript functions”通常意味着你将会使用 C 或 C++代码来调用 JavaScript 中的函数,或者创建新的 JavaScript 函数让 JavaScript 代码去调用。

这里是一些基本的操作,你可能会执行当你使用 N-API 工作时:

调用 JavaScript 函数:

当你想从 C/C++代码调用一个现有的 JavaScript 函数时,你需要先获取到这个函数的引用,然后使用适当的 N-API 函数调用它。举个例子,如果你有一个 JavaScript 函数hello,你想从你的 C/C++插件内部调用它,你的代码可能会像这样:

// 假设 `napi_value js_hello_function` 是之前获取的JavaScript函数引用
napi_value result;
napi_call_function(env, global, js_hello_function, 0, NULL, &result);

在上面的代码中,env是一个表示当前 N-API 环境的变量,而global是全局对象的引用。这行代码实际上就是调用了hello函数,并且没有传递任何参数(因为 args 数量是 0,args 数组是 NULL)。

创建新的 JavaScript 函数:

如果你想创建一个新的函数并将其暴露给 JavaScript 代码使用,你可以通过定义一个 C/C++函数,然后告诉 N-API 它应该被视为 JavaScript 函数。

这是如何完成的一个简化的例子:

// 这是C函数,我们想在JavaScript中以函数形式调用
napi_value MyNativeFunction(napi_env env, napi_callback_info info) {
    // 在这里处理函数调用
    return some_result;
}

// 注册函数使其在JavaScript中可用
napi_value Init(napi_env env, napi_value exports) {
    napi_value my_function;
    napi_create_function(env, "myFunction", NAPI_AUTO_LENGTH, MyNativeFunction, NULL, &my_function);

    // 将生成的函数添加到exports对象中,使其可以被JavaScript模块导出和访问
    napi_set_named_property(env, exports, "myFunction", my_function);

    return exports;
}

在上面的代码片段中,MyNativeFunction是 C 函数。通过调用napi_create_function,我们说“嘿 Node.js,请将此 C 函数视为 JavaScript 函数”,并且 Node.js 做了相应的内部连接。

现在,来自 JavaScript 世界的人可以这样调用这个函数:

const myAddon = require("myAddon");
myAddon.myFunction(); // 调用我们刚刚定义的函数

传递参数给 JavaScript 函数:

如果你需要向 JavaScript 函数传递参数,你可以构建一个包含napi_value类型参数的数组,并将其传递给napi_call_function

举个例子,如果你想传递一个字符串和一个数字:

napi_value arg1, arg2;
napi_create_string_utf8(env, "Hello", NAPI_AUTO_LENGTH, &arg1);
napi_create_double(env, 123, &arg2);

napi_value args[] = {arg1, arg2};
napi_value result;
napi_call_function(env, global, js_hello_function, 2, args, &result);

以上只是 N-API 中与 JavaScript 函数操作相关的基础概念和例子。这些操作能够让你的本地插件与 JavaScript 代码无缝协同工作。记得每次操作都需要检查返回值,确保没有发生错误。

napi_call_functionopen in new window

napi_call_function 是 Node.js 中的一个 N-API 函数,它允许你在 JavaScript 环境中调用已存在的 JavaScript 函数。Node.js 的 N-API 是一个 C API,它提供了从原生代码(如 C 或 C++插件)与 JavaScript 代码进行交互的能力。

为了理解 napi_call_function 的作用,首先需要知道以下几点:

  1. N-API (Node API)是用来构建原生插件的 API,使得你可以在不直接使用 JavaScript 但想要与 Node.js 交互的地方使用它。
  2. 原生插件 是使用 C/C++等编写的模块,它们可以通过 N-API 与 Node.js 通信。
  3. JavaScript 函数调用 指的是在 JavaScript 代码中执行一个函数,并且给这个函数传递参数,当然函数本身可能会返回一些值。

现在,具体到 napi_call_function 函数,其目的就是让你从 C/C++代码中调用 JavaScript 函数。它的基本步骤包括:

  • 确保你有一个 napi_env 环境变量。这个环境变量代表了一个活跃的 Node.js 环境,用于 N-API 调用。
  • 获得一个指向 JavaScript 函数的 napi_value 引用。
  • 准备你想要传递给 JavaScript 函数的参数,每个参数都是一个 napi_value
  • 使用 napi_call_function 来实际调用该函数,并将结果存储在另一个 napi_value 中。

函数原型如下:

napi_status napi_call_function(napi_env env,
                               napi_value recv,
                               napi_value func,
                               size_t argc,
                               const napi_value* argv,
                               napi_value* result);

参数描述:

  • env: 当前的 N-API 环境。
  • recv: 这是调用函数时的 this 值,如果函数不是对象的方法,则可以传递 undefined 或全局对象。
  • func: 一个引用了你想要调用的 JavaScript 函数的 napi_value
  • argc: 传递给函数的参数数量。
  • argv: 参数列表的数组,其中每个元素都是一个 napi_value
  • result: 一个指向 napi_value 的指针,用来接收函数调用的返回值。

举个简单例子,假设我们有一个 JavaScript 函数 add 如下:

function add(a, b) {
  return a + b;
}

在 C/C++中,我们想要调用这个函数并传入两个数字作为参数。以下是使用 napi_call_function 的大致代码示例:

##include `<`node_api.h>

// ...省略其他所需的N-API引导代码...

napi_value addFunc; // 假设这是我们已经获取的JavaScript函数 'add' 的N-API引用
napi_value result;
napi_value args[2];

// 假设n和m是我们想要相加的两个数字
double n = 1.0;
double m = 2.0;

// 创建两个napi_value表示我们要传递给add函数的参数
napi_create_double(env, n, &args[0]);
napi_create_double(env, m, &args[1]);

// 调用函数
napi_status status = napi_call_function(env, global, addFunc, 2, args, &result);

// 处理可能出现的错误
if (status != napi_ok) {
    // 错误处理
}

// 假设函数执行成功,现在result变量中存储了返回值
double sum;
napi_get_value_double(env, result, &sum);

// sum 现在应该等于 3.0,因为 1.0 + 2.0 = 3.0

这段 C/C++代码展示了如何设置参数,如何调用 JavaScript 函数,以及如何获取返回值。这只是一个非常基础的例子,实际上,在真正的原生插件开发中,你可能还需要处理很多额外的边界条件,比如异常处理等。

napi_create_functionopen in new window

napi_create_function是 Node.js 中的一个 API 函数,用于在原生模块(通常是用 C 或 C++编写的扩展)中创建一个新的 JavaScript 函数。这个 API 属于 N-API,它是一个独立于 JavaScript 运行时的接口,允许你构建可以跨不同版本的 Node.js 运行的原生插件。

N-API 旨在减少维护成本,并且通过提供与 Node.js 核心无关的稳定 API 层来提高模块的兼容性。

下面我将解释一下napi_create_function的使用方式,并给出一个简单的示例。

基本使用

当你想要在原生模块中定义一个可以从 JavaScript 代码调用的函数时,你会使用napi_create_function

这个函数的基本签名如下:

napi_status napi_create_function(napi_env env,
                                 const char* utf8name,
                                 size_t length,
                                 napi_callback cb,
                                 void* data,
                                 napi_value* result);

参数解释:

  • env: 这是一个表示 N-API 环境的句柄,它在很多 N-API 调用中都被使用。
  • utf8name: 这是你想要创建的 JavaScript 函数的名称,表示为 UTF-8 编码的字符串。
  • length: 这是上面名称字符串的长度,如果你传递NAPI_AUTO_LENGTH,则函数会自动计算长度。
  • cb: 这是一个指向你的原生函数的指针,当 JavaScript 代码调用你的函数时,这个回调函数会被执行。
  • data: 这是一个指向任意数据的指针,你可以将它设为 NULL 或者传递一些你的函数可能需要的数据。
  • result: 这是一个指向新创建的 JavaScript 函数的指针,函数调用成功后,在此返回新函数。

实际运用的例子

假设我们想在原生模块中创建一个名为"hello"的函数,当从 JavaScript 调用时,它返回字符串"Hello from native module!"。

首先,我们定义原生函数的实现:

##include `<`node_api.h>

napi_value HelloFunction(napi_env env, napi_callback_info info) {
    napi_value greeting;
    napi_status status;

    // 创建一个新的字符串值,用于返回
    status = napi_create_string_utf8(env, "Hello from native module!", NAPI_AUTO_LENGTH, &greeting);
    if (status != napi_ok) {
        // 处理错误...
    }

    return greeting;
}

然后,在模块初始化时,我们使用napi_create_function来创建"hello"函数并将其暴露给 JavaScript:

napi_value Init(napi_env env, napi_value exports) {
    napi_status status;
    napi_value fn;

    // 创建函数
    status = napi_create_function(env, "hello", NAPI_AUTO_LENGTH, HelloFunction, NULL, &fn);
    if (status != napi_ok) {
        // 处理错误...
    }

    // 将创建的函数作为模块exports的属性
    status = napi_set_named_property(env, exports, "hello", fn);
    if (status != napi_ok) {
        // 处理错误...
    }

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

这样,当我们的原生模块被加载到 Node.js 中时,就会有一个名为"hello"的函数可供 JavaScript 代码调用:

const nativeModule = require("./build/Release/native-module.node");

console.log(nativeModule.hello()); // 输出: Hello from native module!

在这个例子中,我们展示了如何使用napi_create_function在原生模块中创建一个简单的函数,以及如何将该函数公开给 Node.js 环境。

napi_get_cb_infoopen in new window

napi_get_cb_info 是 Node.js 中 N-API(原生 API)的一部分,旨在为构建原生插件提供一个稳定的 ABI(应用二进制接口)。这意味着使用 N-API 编写的插件不会因为 Node.js 版本的升级而需要重新编译,从而使得插件更加稳定。

具体来说,napi_get_cb_info 函数用于从原生扩展中的函数调用获取信息。当 JavaScript 调用一个 C/C++ 扩展模块中的函数时,你可以使用 napi_get_cb_info 来获取传递给该函数的参数。

这个函数的定义如下:

napi_status napi_get_cb_info(
    napi_env env,                // [in] 环境句柄
    napi_callback_info cb_info,  // [in] 回调信息
    size_t* argc,                // [in,out] 参数数量
    napi_value* argv,            // [out] 参数数组
    napi_value* this_arg,        // [out] this 指向的对象
    void** data                  // [out] 关联的数据指针
);

在这里:

  • env 是表示当前执行上下文环境的句柄。
  • cb_info 是一个回调信息类型的值,包含了关于当前函数调用的上下文信息。
  • argc 初始时应该设置为你准备接收的参数的最大数量。函数执行完后,它将被设置为实际传递的参数数量。
  • argv 是一个数组,用于接收函数的参数值。数组的大小应至少与 argc 的初始值一样大。
  • this_arg 将被设置为调用函数时的 this 值。
  • data 是指向与当前回调相关联的任何数据的指针。

现在让我们看一个例子:

假设你正在编写一个 Node.js 的原生扩展,其中有一个函数 MyFunction 需要从 JavaScript 代码中获取两个参数:

// JavaScript 调用
const addon = require("./build/Release/addon");
let result = addon.MyFunction(5, 10);

在 C++ 中,你可能会这样实现 MyFunction

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 2;                       // 我们期望接收两个参数
    napi_value argv[2];                    // 创建一个数组来存储这些参数
    napi_value this_arg;
    void* data;

    // 获取函数调用的详细信息
    napi_status status = napi_get_cb_info(env, info, &argc, argv, &this_arg, &data);

    // 检查是否成功获取参数
    if (status != napi_ok || argc `<` 2) {
        // 处理错误情况...
    }

    // 使用参数进行计算或其他操作...

    // 返回结果给 JavaScript
    napi_value result;
    // ... 省略创建和返回结果的代码 ...

    return result;
}

// 注册函数以便在 JS 中可以调用
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "MyFunction", fn);
    return exports;
}

在上面的例子中,MyFunction 通过 napi_get_cb_info 获取了 JavaScript 传递给它的两个参数。然后,你可以处理这些参数并返回某个结果。这个机制允许将 C/C++ 功能暴露给 JavaScript 代码,是构建高性能 Node.js 插件的基础。

napi_get_new_targetopen in new window

Node.js 中的 N-API 是一个用 C 语言编写的底层 API,它允许你创建原生插件。这些原生插件可以直接与 Node.js 的 V8 引擎交互,提供比 JavaScript 更快的性能或访问系统资源和第三方库。

napi_get_new_target 函数是 N-API 的一部分,它用来确定一个函数是否被当作构造函数(使用 new 关键字)调用。

在 JavaScript 中,你可以有两种方式调用函数:

  1. 普通调用:myFunction()
  2. 构造函数调用:new MyConstructor()

当你使用 new 调用函数时,JavaScript 引擎会创建一个新的对象,并将该对象作为函数执行过程中的 this 上下文。如果没有使用 new,通常 this 会引用到全局对象或者是 undefined(在严格模式下)。

napi_get_new_target 允许原生插件检查一个函数是否通过 new 关键字被调用。如果是通过 new 被调用,那么它会返回一个指向新创建对象的引用;如果不是,它将返回 NULL

请看一个示例,假设我们有如下的 C++ 代码作为 Node.js 插件:

##include `<`node_api.h>

napi_value MyFunction(napi_env env, napi_callback_info info) {
    napi_value new_target;
    napi_status status = napi_get_new_target(env, info, &new_target);

    // 检查是否通过 "new" 被调用
    bool is_constructor_call = (status == napi_ok && new_target != nullptr);

    if (is_constructor_call) {
        // 这是一个构造函数调用
        // 可以继续操作 new_target
    } else {
        // 这是一个普通函数调用
        // 相应地处理这种情况...
    }

    // 返回一个值...
}

void Init(napi_env env, napi_value exports) {
    napi_value fn;

    // 创建一个函数
    napi_create_function(env, nullptr, 0, MyFunction, nullptr, &fn);

    // 将函数暴露给模块导出
    napi_set_named_property(env, exports, "myFunction", fn);
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在这个插件中:

  • 我们定义了一个名为 MyFunction 的函数,它可以用作构造函数。
  • 在函数内部,我们使用 napi_get_new_target 检查它是否被当作构造函数调用。
  • 根据是否通过 new 调用,我们可以决定如何处理这次函数调用。

你需要在 JavaScript 中加载和使用这个插件,就像这样:

const myModule = require("myModule");

// 使用 new 调用
const obj = new myModule.myFunction();

// 不使用 new 调用
myModule.myFunction();

在前者的情况中,MyFunction 内部的 is_constructor_call 会是 true,而在后者的情况中,则是 false

总结起来,napi_get_new_target 是一个用于区分函数调用方式的工具,这在创建可以同时作为普通函数和构造函数的原生插件时非常有用。

napi_new_instanceopen in new window

napi_new_instance 是 Node.js 中的一个 N-API 函数,它用于创建 JavaScript 中的新对象实例。N-API 是 Node.js 提供的一个 C API 层,使得原生模块(使用 C 或 C++编写)能够与 JavaScript 代码无缝交互,不受 Node.js 版本变化的影响。

在解释这个函数之前,让我们简单了解几个相关概念:

  1. 原生模块:指的是用 C 或 C++等编程语言编写并可以直接调用 Node.js 或 V8 引擎提供 API 的模块。
  2. JavaScript 实例:在 JavaScript 中,当你通过new关键字调用构造函数时,你会创建该构造函数的一个实例(即一个对象)。
  3. N-API:Node.js 提供的一组用 C 编写的 API,使得开发者可以构建原生插件,并保证跨 Node.js 版本的稳定性。

现在,让我们详细了解napi_new_instance函数。

napi_new_instance

这个函数用于创建一个由原生模块定义的构造函数的新实例。例如,如果你有一个 C++类并且想要从 JavaScript 代码中创建它的实例,就可以使用napi_new_instance来做到这一点。

函数签名

napi_status napi_new_instance(napi_env env,
                              napi_value constructor,
                              size_t argc,
                              const napi_value* argv,
                              napi_value* result);
  • env: 当前的 N-API 环境句柄。
  • constructor: JavaScript 中的函数,通常是你想要实例化的构造函数。
  • argc: 传递给构造函数的参数数量。
  • argv: 指向传递给构造函数的参数数组的指针。
  • result: 此函数调用完成后,将创建的新实例的句柄写入此位置。

返回值

返回napi_status,这表明函数调用成功还是失败。如果成功,新创建的实例将被赋值给result参数。

实际运用示例

假设你有一个 C++类叫MyObject,它有一个构造函数可以接收一个整数。你希望在 Node.js 中创建MyObject的实例,并传递一个数字作为参数。

首先,你会创建一个 N-API 的包装器,以便 JavaScript 可以调用它:

// C++ 端的 MyObject 类
class MyObject {
public:
    MyObject(int value) : value_(value) {}
    int GetValue() const { return value_; }
private:
    int value_;
};

// 包装器函数,JavaScript 中可以调用它来创建 MyObject 实例
napi_value CreateMyObjectInstance(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value thisArg;
    void* data;

    // 获取JavaScript调用时传递的参数
    napi_get_cb_info(env, info, &argc, args, &thisArg, &data);

    // 假设我们已经有了一个指向MyObject构造函数的napi_value 'constructor'
    napi_value instance;
    napi_status status = napi_new_instance(env, constructor, argc, args, &instance);
    if (status != napi_ok) {
        // 处理错误情况
    }

    return instance; // 返回新创建的MyObject实例
}

然后,在 JavaScript 中,你可以这样使用它:

const myModule = require("native-addon"); // 加载原生模块
let myObject = myModule.createMyObjectInstance(42); // 创建一个MyObject实例

在上面的例子中,createMyObjectInstance会对应于 C++端的CreateMyObjectInstance函数。当你从 JavaScript 调用这个函数时,它会使用给定的参数(在这里是数字 42)创建一个新的MyObject实例。

Object wrapopen in new window

Node.js 中的Object Wrap是指使用 N-API(一组 C 语言 API)来绑定 C++对象与 JavaScript 对象。它允许你创建一个 C++对象,并在 JavaScript 代码中操作这个对象,就好像它是一个普通的 JavaScript 对象一样。

在 Node.js 中创建扩展的时候,我们可能需要从 JavaScript 调用 C++代码以利用其性能优势或者访问系统资源。但因为 JavaScript 和 C++是两种不同的编程语言,直接交互并非易事。为此,Node.js 提供了 N-API 这样的桥梁,使得这两种语言能够互相沟通。

Object Wrap 的基本原理:

  1. C++侧创建类:首先在 C++侧定义一个类,并且为这个类定义构造函数、析构函数和一些方法。
  2. 包装 C++对象:然后通过 Object Wrap 将这个 C++类的实例关联到一个新建的 JavaScript 对象上。
  3. 在 JavaScript 中调用:JavaScript 代码可以创建和使用这个对象,调用其方法,而实际上运行的是 C++代码。

举例说明:

假设我们要在 Node.js 中创建一个简单的 C++ Counter 类,该类有增加(increment)、减少(decrement)和获取值(getValue)的功能。

  1. 定义 C++类:
##include `<`napi.h>

class Counter : public Napi::ObjectWrap`<`Counter> {
public:
    static Napi::Object Init(Napi::Env env, Napi::Object exports); // 初始化函数
    Counter(const Napi::CallbackInfo& info); // 构造函数

private:
    static Napi::FunctionReference constructor; // 构造函数引用
    double value_; // 私有变量,计数器的值
    Napi::Value Increment(const Napi::CallbackInfo& info); // 实现增加
    Napi::Value Decrement(const Napi::CallbackInfo& info); // 实现减少
    Napi::Value GetValue(const Napi::CallbackInfo& info); // 获取当前值
};
  1. 实现方法:
// 此处省略包装方法初始化和构造函数的具体实现
//...

Napi::Value Counter::Increment(const Napi::CallbackInfo& info) {
    value_ += 1;
    return Napi::Number::New(info.Env(), value_);
}

Napi::Value Counter::Decrement(const Napi::CallbackInfo& info) {
    value_ -= 1;
    return Napi::Number::New(info.Env(), value_);
}

Napi::Value Counter::GetValue(const Napi::CallbackInfo& info) {
    return Napi::Number::New(info.Env(), value_);
}
  1. 创建和导出模块:
Napi::Object Init(Napi::Env env, Napi::Object exports) {
    Counter::Init(env, exports);
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)
  1. 在 JavaScript 中使用:

以上 C++代码编译成 Node.js 扩展后,你可以在 JavaScript 中这样使用它:

const counterAddon = require("./build/Release/counter-addon.node");
const myCounter = new counterAddon.Counter(0);

console.log(myCounter.getValue()); // 输出0
myCounter.increment();
console.log(myCounter.getValue()); // 输出1
myCounter.decrement();
console.log(myCounter.getValue()); // 输出0

在这个例子中,我们创建了一个 C++的Counter类,并且它有三个方法。当我们在 JavaScript 中调用这些方法时,实际上是在 C++层面执行相关操作的。通过这种方式,我们可以利用 C++的性能和特性来扩展 Node.js 的功能。

记住,N-API 是稳定的跨版本的 API 集合,意味着用它写的扩展可以在未来的 Node.js 版本中无需重新编译。这使得维护和升级 Node.js 扩展变得更加容易。

napi_define_classopen in new window

napi_define_class 是 Node.js 中 N-API(原生 API)的一部分,它让你能够创建和定义一个新的 JavaScript 类,该类可以链接到 C 或者 C++ 的代码。这样做的好处是你可以在 Node.js 中使用性能更高、更接近硬件层面的编程语言写的代码。

当我们说“定义一个类”时,指的是在 JavaScript 中创建一个可以被实例化(即创建具体对象)的蓝图。这个类可以有自己的属性(properties)和方法(methods),就像其他任何 JavaScript 类一样。

下面我们来详细解释一下 napi_define_class 函数,并举几个例子来说明它的运用。

首先,这个函数的基本形式是:

napi_status napi_define_class(
    napi_env env,
    const char* utf8name,
    size_t length,
    napi_callback constructor,
    void* data,
    size_t property_count,
    const napi_property_descriptor* properties,
    napi_value* result);

这里各个参数的含义是:

  • env: 当前的 N-API 环境句柄。
  • utf8name: 新类的名称,它是一个 UTF-8 编码的字符串。
  • length: 这个名称的长度,如果是以 null 结尾的字符串,则可以传递 NAPI_AUTO_LENGTH
  • constructor: 一个指向构造函数的指针,这个构造函数会在你通过 new 关键字创建类的实例时调用。
  • data: 可选的数据,可以和构造函数一起传递,通常用于保存构造函数需要的状态或信息。
  • property_count: 类属性的数量。
  • properties: 类属性描述符数组,每个属性都对应一个 napi_property_descriptor 结构体,用于定义属性的名字、属性存在的位置、以及属性的访问器等。
  • result: 这个函数执行完毕后,存放结果的地方,通常是新定义类的引用。

现在,让我们看一个简单的例子:

想象一下,我们要在 Node.js 中创建一个原生扩展,表示一个简单的 "Rectangle" (矩形)类。这个类将拥有长度和宽度作为属性,并且有一个方法来计算面积。

在 C/C++ 代码中,你可能会这样定义 Rectangle 类:

##include `<`node_api.h>

// 构造函数
napi_value RectangleConstructor(napi_env env, napi_callback_info info) {
    // ... 创建和初始化 Rectangle 对象 ...
}

// 计算面积的方法
napi_value CalculateArea(napi_env env, napi_callback_info info) {
    // ... 计算面积的代码 ...
}

// 初始化函数,用于创建类和导出相关功能
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;

    // 属性描述符:length 和 width
    napi_property_descriptor properties[] = {
        { "length", 0, 0, 0, 0, 0, napi_default, 0 },
        { "width", 0, 0, 0, 0, 0, napi_default, 0 }
    };

    // 定义 Rectangle 类
    napi_value rectangle_class;
    status = napi_define_class(env, "Rectangle", NAPI_AUTO_LENGTH, RectangleConstructor, nullptr, 2, properties, &rectangle_class);

    // 将 Rectangle 类导出给 JavaScript 使用
    if (status == napi_ok) {
        status = napi_set_named_property(env, exports, "Rectangle", rectangle_class);
    }

    return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

上述代码为了简洁并未包含完整的错误处理和实际的属性/方法实现细节,但它展示了如何使用 napi_define_class 来创建一个新的类。在实际应用中,你需要实现构造函数、面积计算方法,并确保所有的属性和方法都正确关联到对应的 C/C++ 函数。

最后,当你编译并加载了这个扩展之后,在 Node.js 中可以这样使用:

const myNativeAddon = require("my-native-addon");
const Rectangle = myNativeAddon.Rectangle;

let rect = new Rectangle();
rect.length = 10;
rect.width = 5;

console.log(rect.CalculateArea()); // 输出矩形的面积

这就是 napi_define_class 在 Node.js v21.7.1 版本中的作用和一个基础的示例。

napi_wrapopen in new window

好的,让我们来谈谈 Node.js 中的 napi_wrap

在 Node.js 中,N-API(Node.js API)是一个低层次的 API,它允许你编写本地插件,即用 C 或者 C++ 等语言编写的模块。这些本地插件可以直接操作 JavaScript 值和对象,以及与 JavaScript 代码交互。

napi_wrap 是 N-API 所提供的一个函数,它的作用是将一个 JavaScript 对象与一个本地对象(通常是用 C/C++ 编写的对象)关联起来。通过这种关联,你可以在本地代码中保存私有数据,并且确保当 JavaScript 对象被垃圾回收时,相关的本地资源也能得到清理。

现在让我们详细了解一下 napi_wrap 的参数和使用方法:

函数原型

napi_status napi_wrap(
    napi_env env,
    napi_value js_object,
    void* native_object,
    napi_finalize finalize_cb,
    void* finalize_hint,
    napi_ref* result);
  • env: 这是表示当前执行环境的 napi_env 句柄。
  • js_object: 这是要关联本地对象的 JavaScript 对象。
  • native_object: 这是要附加到 JavaScript 对象上的本地对象的指针。
  • finalize_cb: 当 JavaScript 对象被垃圾回收时调用的回调函数,用来清理 native_object
  • finalize_hint: 传递给 finalize_cb 回调的可选数据。
  • result: 返回创建的引用,这是一个可选参数。

使用场景

假设你想创建一个 JavaScript 类,称为 MyNativeObject,它背后有一个 C++ 类 NativeObject 提供支持。你可能会这样使用 napi_wrap:

##include `<`node_api.h>

// 假设这是你的 C++ 对象的类
class NativeObject {
public:
    // 构造函数、析构函数和其他方法
};

// 当 JS 对象被回收时需要调用的清理函数
void FinalizeCallback(napi_env env, void* finalize_data, void* finalize_hint) {
    // 转换 finalize_data 为 NativeObject 指针并删除它
    NativeObject* obj = static_cast`<`NativeObject*>(finalize_data);
    delete obj;
}

// 创建新的 MyNativeObject 实例的函数
napi_value CreateMyNativeObject(napi_env env, napi_callback_info info) {
    napi_status status;

    // 从 JavaScript 端接收的参数等
    size_t argc = 1;
    napi_value args[1];
    status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    // 创建你的 C++ 对象
    NativeObject* obj = new NativeObject();

    // 创建一个新的空 JavaScript 对象
    napi_value js_object;
    status = napi_create_object(env, &js_object);

    // 将你的本地对象与 JavaScript 对象关联起来
    status = napi_wrap(env, js_object, obj, FinalizeCallback, nullptr, nullptr);

    // 返回新创建的 JavaScript 对象
    return js_object;
}

// 初始化函数,设置 MyNativeObject
napi_value Init(napi_env env, napi_value exports) {
    napi_status status;

    // 设置 CreateMyNativeObject 为导出函数
    napi_value new_function;
    status = napi_create_function(env, "", NAPI_AUTO_LENGTH, CreateMyNativeObject, nullptr, &new_function);
    status = napi_set_named_property(env, exports, "createMyNativeObject", new_function);

    return exports;
}

// 定义模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

以上就是 napi_wrap 的一个实际应用示例,在这个例子中我们定义了一个 C++ 类 NativeObject 并在 JavaScript 环境中创建了一个对应的类 MyNativeObject。每当在 JavaScript 中创建 MyNativeObject 的实例时,都会在底层创建一个 NativeObject 实例并将其与 JavaScript 对象关联起来。当 JavaScript 对象被垃圾回收时,FinalizeCallback 函数会被调用,它将负责清理 C++ 对象,防止内存泄露。

napi_unwrapopen in new window

napi_unwrap是 Node.js 中 N-API(Node API)的一个函数,它使得原生模块开发人员能够把 JavaScript 对象与 C/C++的原生对象相关联并且管理这种关联。换句话说,你可以在一个 JavaScript 对象中隐藏一个指向 C/C++对象的指针,并且当需要时,再从 JavaScript 对象中检索出这个指针。这种技术经常用于创建和维护 JavaScript 对象与 C/C++资源之间的联系。

使用napi_unwrap的典型场景涉及以下步骤:

  1. 使用napi_wrap将一个 C/C++对象(通常是一个类的实例)与一个新创建的或者已有的 JavaScript 对象关联起来。
  2. 在 JavaScript 代码执行期间,当需要访问这个 C/C++对象时,可以调用napi_unwrap来获取之前存储的指针。
  3. 使用这个指针,你可以在原生代码中操作对应的 C/C++对象。

下面是一个简化的例子,说明了如何使用napi_unwrap

// 假设你有一个C++类 MyObject
class MyObject {
  public:
    void DoSomething() {
      // ... 实际的操作 ...
    }
};

// 这是一个N-API函数,它将JavaScript对象与C++对象关联起来
napi_value CreateMyObject(napi_env env, napi_callback_info info) {
  // 创建一个C++对象
  MyObject* obj = new MyObject();

  // 获取JavaScript上下文和传递给函数的参数(如果有的话)
  size_t argc = 0;
  napi_value thisArg;
  napi_get_cb_info(env, info, &argc, nullptr, &thisArg, nullptr);

  // 将C++对象与JavaScript对象关联起来
  napi_wrap(env, thisArg, obj, MyObjectFinalizer, nullptr, nullptr);

  // 返回JavaScript对象
  return thisArg;
}

// 这部分展示了如何在另一个函数中解析出C++对象
napi_value CallDoSomething(napi_env env, napi_callback_info info) {
  size_t argc = 0;
  napi_value thisArg;
  napi_get_cb_info(env, info, &argc, nullptr, &thisArg, nullptr);

  // 解析出C++对象
  MyObject* obj;
  napi_unwrap(env, thisArg, reinterpret_cast`<`void**>(&obj));

  // 调用C++对象的方法
  obj->DoSomething();

  // 返回undefined,因为这个函数不需要返回值
  napi_value result;
  napi_get_undefined(env, &result);
  return result;
}

在这个例子中,假设我们有一个叫作MyObject的 C++类,其中包含了一个方法DoSomething。我们想要让 JavaScript 代码能够创建这个类的实例,并且调用其DoSomething方法。

  • CreateMyObject函数中,我们创建了一个MyObject的实例,并使用napi_wrap将其与 JavaScript 的thisArg对象关联。
  • 然后,在CallDoSomething函数中,我们使用napi_unwrapthisArg中取回之前存储的MyObject实例的指针,然后调用其DoSomething方法。

注意这里只是展示了如何使用这两个 API,详细实现会涉及错误处理、资源清理等其他考虑。

重要的是要记住,napi_wrapnapi_unwrap主要被用于管理 JavaScript 对象和 C/C++资源之间的生命周期和关系。 结合使用这两个函数,可以确保当 JavaScript 对象被垃圾回收时,相应的 C/C++资源也得到适当的释放。

napi_remove_wrapopen in new window

Node.js 中的 N-API 是一个底层的 API,使得你可以用 C 或者 C++ 编写扩展。这些扩展可以和 Node.js 进行交互,让你能够以非常高效的方式执行一些不能或不方便直接用 JavaScript 实现的任务。

napi_remove_wrap 是 N-API 的一个函数,它的作用是移除之前使用 napi_wrap 对一个 JavaScript 对象所附加的原生(native)指针。在详细解释这个概念之前,我们需要先理解几个和 N-API 相关的概念:

  1. Native 指针(原生指针): 这通常是一个指向 C/C++ 数据结构的指针。
  2. Wrap(包裹): 在 N-API 中,“包裹”一个对象是指将一个 Native 指针和一个 JavaScript 对象相关联。这样做可以让 JavaScript 对象与 C/C++ 代码共享状态。
  3. Unwrap(解包): 解包则是取回之前包裹的原生指针。

现在,来看napi_remove_wrap这个函数。当你在 JavaScript 和 C/C++ 混合编程时,你可能会创建一些复杂的对象,这些对象在 JavaScript 层面上看起来只是普通的对象,但实际上它们背后有着 C/C++ 的原生资源(比如内存、文件描述符等)。通过 napi_wrap 函数,你可以将这些原生资源和 JavaScript 对象关联起来,这称为“包裹”。

然而,有时候你希望释放这些原生资源,例如,当对象不再需要时,或者你想重新分配资源到另一个对象上。这时候就需要用到napi_remove_wrap。调用napi_remove_wrap会做两件事情:

  1. 它会移除 JavaScript 对象和原生指针之间的关联。
  2. 它允许你获取那个原生指针,这样你就可以适当地管理它,比如释放它占用的内存。

例子

假设我们有一个 C++ 类 MyObject,并且我们想要从 JavaScript 访问它的实例。我们首先会用 napi_wrapMyObject 的实例(原生指针)包裹到一个新的 JavaScript 对象中。稍后,如果我们想删除该对象,并且清理后台的 C++ 资源,我们会使用 napi_remove_wrap 来取得原生指针,然后删除它。

##include `<`node_api.h>

class MyObject {
public:
    MyObject() { /* 构造函数逻辑 */ }
    ~MyObject() { /* 析构函数逻辑,负责释放资源 */ }
    // ...
};

// 包裹 MyObject 实例到一个 JavaScript 对象
napi_status WrapMyObject(napi_env env, MyObject* obj, napi_value jsObj) {
    return napi_wrap(env, jsObj, obj, MyObject::Destructor, nullptr, nullptr);
}

// 之后某个时间,我们想要移除包裹并且清理资源
napi_status RemoveWrapMyObject(napi_env env, napi_value jsObj) {
    MyObject* obj;
    napi_status status = napi_remove_wrap(env, jsObj, reinterpret_cast`<`void**>(&obj));

    if (status == napi_ok) {
        delete obj; // 调用 MyObject 的析构函数来清理
    }

    return status;
}

请记住,以上的例子只是一个简化的演示,实际当中处理错误情况和生命周期管理会更加复杂。此外,在 Node.js 项目中直接编辑 C++ 代码并不常见,因为 Node.js 的核心优势在于其事件驱动和非阻塞的 I/O 模型,并且大多数常见任务都可以直接用 JavaScript 完成。不过,了解这些能力对于性能优化和编写更复杂的扩展可能是很有帮助的。

napi_type_tag_objectopen in new window

当然,我会尽量用简单易懂的语言来解释。napi_type_tag_object 是 Node.js 中 N-API(原生 API)的一部分,它允许你在 JavaScript 与原生 C/C++ 插件之间安全地传递对象。

为了理解 napi_type_tag_object 的作用,首先需要明白在 Node.js 中,JavaScript 代码可以调用 C/C++ 编写的原生模块,这个过程涉及到数据类型的转换。因为 JavaScript 和 C/C++ 使用不同的类型系统,所以在它们之间传递复杂的数据(比如对象)时,我们需要确保传递的是正确的类型,防止发生意外的行为或者安全问题。

napi_type_tag_object 就是用来帮助标识和验证对象类型的。通过给对象添加一个唯一的“类型标签”,你可以在后续的代码中检查传入的对象是否拥有正确的类型标签,这样就可以确认它是从预期的源头传入的,而不是其他可能造成问题的对象。

来举几个例子:

假设你有一个原生的 C/C++插件,它提供了一个函数来创建一个特定类型的对象,比如表示一个网络连接的对象。你可以使用napi_type_tag_object来标记这个对象:

napi_value connection_object;
napi_create_object(env, &connection_object);

// 创建一个唯一的类型标签
napi_type_tag type_tag;
napi_create_type_tag(env, &type_tag);

// 将类型标签附加到刚刚创建的对象上
napi_type_tag_object(env, connection_object, &type_tag);

然后在另一个函数中,你可能想操作这个网络连接对象。但是你需要确保传进来的对象确实是一个网络连接对象,而不是随便哪个 JS 对象:

napi_status status = napi_check_object_type_tag(env, args[0], &type_tag, &matches);
if (status == napi_ok && matches) {
    // 确认 args[0] 就是我们预期的那种网络连接对象
    // 可以安全地进行进一步的操作
} else {
    // 抛出错误,告诉调用者传入的不是有效的网络连接对象
}

这样,即使 JavaScript 用户尝试传入一个普通的 JavaScript 对象来欺骗你的插件,由于没有匹配的类型标签,你的插件能识别并拒绝这种错误的使用方式,保护了代码的安全性。

总结一下,napi_type_tag_object 是 Node.js N-API 提供的一个功能,用于原生模块开发中更安全地处理和验证 JavaScript 对象。通过给对象添加和校验类型标签,程序员可以确保他们的原生函数只处理那些被标记为正确类型的对象,避免了潜在的类型错误和安全问题。

napi_check_object_type_tagopen in new window

好的,Node.js 中的 N-API 是一个用于创建本地插件的 API,它允许你在 Node.js 环境中使用 C 或 C++ 代码。通过 N-API,开发者可以编写能够与 JavaScript 代码互操作的高性能本地模块。

napi_check_object_type_tag 是 N-API 中的一个函数,其作用是帮助你验证一个 JavaScript 对象是否拥有一个特定的标记(tag)。在这里,“标记”是一个唯一标识符,用以区分不同类型的对象,确保你处理的是正确类型的对象。

函数原型:

napi_status napi_check_object_type_tag(napi_env env,
                                       napi_value object,
                                       const napi_type_tag* type_tag,
                                       bool* result);
  • napi_env env: 当前的 N-API 环境句柄。
  • napi_value object: 待检查的 JavaScript 对象。
  • const napi_type_tag* type_tag: 指向你之前定义的 napi_type_tag 结构体的指针。
  • bool* result: 函数执行后,通过这个指针返回检查结果(truefalse)。

使用场景:

假设你正在编写一个本地模块,该模块提供了一个自定义的文件读取器,你希望确保用户传递给你的对象是你认可的“文件读取器对象”。

首先,你会在你的模块初始化时为你的“文件读取器对象”定义一个 napi_type_tag

napi_type_tag file_reader_type_tag = {0};

当创建新的“文件读取器对象”时,你会将这个标记和对象关联起来:

napi_value create_file_reader(napi_env env) {
    // 创建对象...
    napi_value file_reader;
    // ...创建对象的代码...

    // 关联标记到对象上
    napi_status status = napi_type_tag_object(env, file_reader, &file_reader_type_tag);
    // 处理状态代码...

    return file_reader;
}

之后,当要在另一个函数中使用这个对象时,你可以使用 napi_check_object_type_tag 来验证对象是否是一个有效的“文件读取器对象”:

napi_status read_file(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_value this_arg;
    void* data;

    // 获取JavaScript提供的参数
    napi_get_cb_info(env, info, &argc, args, &this_arg, &data);

    // 检查传入的对象是否是我们期待的文件读取器对象
    bool is_file_reader;
    napi_check_object_type_tag(env, args[0], &file_reader_type_tag, &is_file_reader);

    if (!is_file_reader) {
        // 如果不是,抛出错误或进行其他处理...
    }

    // 是文件读取器对象,继续执行读取文件的操作...
}

以上就是 napi_check_object_type_tag 的基本用法。注意,这种类型检查主要用于增强本地模块的安全性和健壮性,以确保 API 的使用者按照预期传递了正确类型的对象。

napi_add_finalizeropen in new window

Node.js 中的 napi_add_finalizer 函数是一个属于 N-API 的 API,用来添加与一个 JavaScript 对象相关联的本地资源的终结器(或称为析构函数)。当这个 JavaScript 对象被垃圾回收时,会自动调用这个终结器函数来清理与之关联的本地资源。

N-API 是 Node.js 提供的一套 C API,它允许原生模块的作者编写独立于 Node.js 版本的代码。使用 N-API 创建的原生模块可以在不同版本的 Node.js 之间无需重新编译就能运行,大大提高了模块的兼容性和稳定性。

先解释几个概念:

  1. JavaScript 对象:在 JavaScript 中创建的对象,可以存储数据和功能。
  2. 本地资源:使用 C 或 C++ 等语言分配的资源,如内存、文件句柄或其他系统资源。
  3. 终结器(Finalizer):一个特殊的函数,它被设计为在对象不再需要时执行,用于释放对象所占用的资源。

现在,我们来看看 napi_add_finalizer 的作用以及如何使用它。

作用:

napi_add_finalizer 的主要作用是确保当一个与原生资源相关联的 JavaScript 对象被垃圾回收时,这些原生资源也得到适当的清理和释放,防止内存泄漏等问题。

使用场景示例:

示例 1: 清理动态分配的内存

假设你在创建一个原生模块,这个模块需要在 C++ 中分配一块动态内存给一个 JavaScript 对象使用。

##include `<`node_api.h>

void* dynamicMemory;
size_t memorySize = 1024; // 假设我们要分配 1024 字节

// 分配内存
dynamicMemory = malloc(memorySize);

// 这里你可能会把 dynamicMemory 传递到 JavaScript 层面的某个对象中

为了确保当 JavaScript 对象不再使用时,这块内存能够被正确地释放,你需要定义一个终结器函数:

void finalize_callback(napi_env env, void* finalize_data, void* finalize_hint) {
    // 清理动态分配的内存
    free(finalize_data);
}

然后你会使用 napi_add_finalizer 将这个终结器函数关联到相应的 JavaScript 对象上:

napi_value jsObject;
// ... 创建或获取一个 jsObject ...

// 添加终结器
napi_add_finalizer(env, jsObject, dynamicMemory, finalize_callback, nullptr, nullptr);

jsObject 被垃圾回收器回收时,finalize_callback 函数将被自动调用,从而释放之前分配的内存。

示例 2: 清理打开的文件描述符

如果你的模块打开了文件,并且将文件描述符保存在了一个 JavaScript 对象中,你同样需要确保文件最终被关闭。

int fileDescriptor = open("somefile.txt", O_RDONLY);

// 假设 fileDescriptor 被保存到了某个 jsObject 中

你需要定义一个适当的终结器来关闭这个文件描述符:

void finalize_file_descriptor(napi_env env, void* finalize_data, void* finalize_hint) {
    int fd = *(int*)finalize_data;
    close(fd); // 关闭文件描述符
}

然后,如同内存的例子那样,使用 napi_add_finalizer 关联终结器:

// 添加终结器
napi_add_finalizer(env, jsObject, &fileDescriptor, finalize_file_descriptor, nullptr, nullptr);

这样,当包含文件描述符的 JavaScript 对象被回收时,finalize_file_descriptor 将被调用,文件将被关闭,避免了文件描述符泄漏。

注意事项:

  • 当使用 napi_add_finalizer 时,你需要确保正确地管理本地资源的生命周期,确保不会在资源被清理后尝试访问它们。
  • 使用 N-API 需要对 C/C++ 和 Node.js 的内部机制有一定的理解。

希望这个解释有助于你理解 napi_add_finalizer 在 Node.js 中的作用和使用方式。

node_api_post_finalizeropen in new window

node_api_post_finalizer 是 Node.js 中的 N-API(原生 API)功能,它允许开发者在创建和使用原生扩展时,确保资源能够被妥善清理。为了解释这个概念,我们需要先了解一些背景知识。

在 Node.js 中,你可以编写 JavaScript 来执行各种任务,但有时候你可能需要更直接地访问底层系统资源或者进行更高性能的操作。这时,你就可以通过编写 C 或 C++ 的代码来创建所谓的“原生模块”,这样就能在 Node.js 项目中使用这些底层能力。

当使用这些原生模块时,经常会在原生代码中分配一些系统资源,比如内存、文件描述符或网络套接字等。正确地管理这些资源非常关键,因为如果不适当地释放资源,就可能导致内存泄漏或其他问题,影响应用程序的性能和稳定性。

这里的 node_api_post_finalizer 就是一个帮助函数,它使得开发者能够注册一个“终结器”(finalizer),也就是当一个原生对象即将被垃圾回收时将调用的函数。这个终结器负责清理与原生对象相关联的资源。

下面我举一个实际例子来说明如何使用 node_api_post_finalizer

假设你在创建一个原生模块,该模块需要打开一个文件,并在操作完毕后关闭文件。在这个过程中,你会在原生代码中创建一个表示文件的对象,并且希望在这个对象不再需要时自动关闭文件。

// 假设这是我们的原生对象结构体
typedef struct {
  FILE* file;
} MyFileHandle;

// 这是我们的终结器函数
void Finalizer(napi_env env, void* finalize_data, void* finalize_hint) {
  MyFileHandle* handle = (MyFileHandle*)finalize_data;
  fclose(handle->file); // 关闭文件
  free(handle); // 释放分配的内存
}

// 创建一个新的 MyFileHandle 对象并与 JavaScript 对象关联
napi_value CreateMyFileHandle(napi_env env) {
  napi_value js_object;
  MyFileHandle* my_file_handle = (MyFileHandle*)malloc(sizeof(MyFileHandle));

  // 打开文件,只是举例,具体参数需根据实际情况填写
  my_file_handle->file = fopen("example.txt", "r");

  // 创建一个新的空对象
  napi_create_object(env, &js_object);

  // 将我的文件处理对象与 JavaScript 对象关联,并指定终结器
  napi_wrap(env,
            js_object,
            (void*)my_file_handle,
            Finalizer,
            NULL, // 可选的提示数据
            NULL); // 不需要此处的返回值

  return js_object;
}

上述代码演示了如何使用 node_api_post_finalizer(在示例中表现为 napi_wrap 函数的一部分)。这个函数确保当 JavaScript 对象被垃圾回收时,我们的终结器函数 Finalizer 将被调用,从而关闭文件并释放分配的内存,避免资源泄漏。

请注意,本例中没有直接使用 node_api_post_finalizer 函数,因为通常它是通过 napi_wrap 隐式调用的。napi_wrap 用于将原生资源包装到 JavaScript 对象中,同时注册一个终结器以便资源可以在不需要时释放。

需要注意的是,node_api_post_finalizer 实际上是 Node.js 文档中的一个命名错误或者说是笔误,正确的名称应该是 napi_add_finalizer。这可能会导致混淆,所以开发者应该查找最新的官方文档来确认正确的 API 名称和使用方法。

Simple asynchronous operationsopen in new window

Node.js 中的 N-API 是一个用于构建本地插件(也就是使用 C 或 C++编写的扩展模块)的 API。在 Node.js v21.7.1 的文档中,"Simple asynchronous operations"部分解释了如何使用 N-API 执行异步操作。

异步操作是指程序可以启动一个任务,在等待这个任务完成的同时继续执行其他代码,而不是停下来等待结果。这对于不阻塞 Node.js 主线程特别重要,因为 Node.js 是单线程的,长时间阻塞会导致整个应用程序无法响应其他任务。

在 N-API 中实现简单的异步操作一般需要以下几步:

  1. 定义工作结构: 首先,你需要创建一个napi_work的实例,这代表了一个异步任务。

  2. 创建异步工作: 使用napi_create_async_work函数创建异步工作,其中你将指定完成这项任务时要调用的函数和销毁资源时要调用的函数。

  3. 将工作排队: 使用napi_queue_async_work函数将定义的工作添加到事件循环中,使其在未来某个时间点执行。

  4. 工作执行: Node.js 会在后台线程池中执行此工作,而不会影响主线程的执行。

  5. 工作完成: 一旦后台任务完成,相关联的完成回调函数将被加入到事件循环中,并在主线程上执行。

  6. 销毁工作: 完成该工作后,你需要使用napi_delete_async_work清理分配的资源。

现在,让我们举一个实际的例子。假设你在开发一个 Node.js 的本地插件,该插件需要进行文件系统的操作,例如读取一个大文件。文件读取是一个耗时的过程,所以你希望以异步方式来处理它,以免阻塞主线程。

##include `<`node_api.h>

// 伪代码 - 执行异步任务的函数
void Execute(napi_env env, void* data) {
    // 这里你可以执行类似读取大文件的操作
    // 注意这个函数是在另一个线程中运行的,不会阻塞主线程
}

// 伪代码 - 异步任务完成后的回调函数
void Complete(napi_env env, napi_status status, void* data) {
    // 这里你可以处理执行结果,比如将文件内容传给JavaScript
    // 这个函数会在主线程中被调用
}

// 伪代码 - 启动异步任务的JS绑定函数
napi_value StartAsyncTask(napi_env env, napi_callback_info info) {
    napi_value work_name;
    napi_create_string_utf8(env, "workName", NAPI_AUTO_LENGTH, &work_name);

    napi_async_work work;
    napi_create_async_work(env, NULL, work_name, Execute, Complete, NULL, &work);
    napi_queue_async_work(env, work);

    return NULL;
}

// 初始化函数,在加载插件时调用
NAPI_MODULE_INIT() {
    napi_value asyncTaskFunction;
    napi_create_function(env, NULL, 0, StartAsyncTask, NULL, &asyncTaskFunction);

    napi_set_named_property(env, exports, "startAsyncTask", asyncTaskFunction);
    return exports;
}

在这个例子中,我们定义了一个StartAsyncTask函数,它会启动一个异步操作来执行耗时的任务(例如读取大文件)。当任务开始时,会立即返回,从而不阻塞 Node.js 的主线程。完成后,会调用Complete函数来处理结果,比如将读取的数据通过事件或回调传递给 JavaScript 层面的代码。

这样,你就可以在 Node.js 中利用 N-API 进行高效的异步操作,而不会影响应用程序的性能和响应性。

napi_create_async_workopen in new window

当你使用 Node.js 编程时,有时候你会需要执行一些耗时的操作,比如访问文件系统、进行网络通信或者做一些复杂的计算。如果这些操作在主线程中执行,它们会阻塞其他代码运行,因为 JavaScript 是单线程的。为了解决这个问题,Node.js 提供了异步编程模型。

其中一个工具就是 N-API 中的napi_create_async_work函数。这个函数允许你创建一个可以在后台执行的任务,这样你的程序就可以继续执行其他任务,而不必等待这个耗时任务完成。

如何使用 napi_create_async_work

要使用napi_create_async_work,你需要准备以下几个东西:

  1. Execute Function: 这个函数包含了你想要异步执行的代码。
  2. Complete Function: 当异步执行完成后,这个函数将被调用。
  3. Data: 一个指向你的数据的指针,这些数据将被传递给 Execute Function 和 Complete Function。
  4. Async Work: 一个引用,代表了这个异步任务。

创建异步任务大致的代码示例如下:

##include `<`node_api.h>

// 执行函数,将在后台线程上运行
void Execute(napi_env env, void* data) {
    // ...耗时的操作...
}

// 完成函数,当任务完成时,在主线程上运行
void Complete(napi_env env, napi_status status, void* data) {
    // ...清理工作或者回调JavaScript代码...
}

// 创建异步工作
napi_value CreateAsyncWork(napi_env env, napi_callback_info info) {
    napi_value work_name;
    napi_create_string_utf8(env, "MyWork", NAPI_AUTO_LENGTH, &work_name);

    napi_async_work work;
    napi_create_async_work(env, NULL, work_name,
                           Execute, Complete, NULL, &work);

    // 将异步工作队列化
    napi_queue_async_work(env, work);

    return NULL;
}

下面是一个更实际的例子:

假设你正在编写一个 Node.js 扩展,需要在 C++层面读取一个大文件,并且希望这个操作不会阻塞主线程。

// 假设你已经包含了所需的头文件和设置好了环境

// 你可能定义了一个结构体来保存你的数据
typedef struct {
    char* filename;
    char* fileContents;
} FileReadData;

void Execute(napi_env env, void* data) {
    FileReadData* readData = (FileReadData*)data;

    // 这里伪代码表示打开文件并读取内容
    readData->fileContents = ReadFile(readData->filename);
}

void Complete(napi_env env, napi_status status, void* data) {
    FileReadData* readData = (FileReadData*)data;

    // 伪代码表示将读取到的内容传递给JavaScript的回调函数
    PassToJSCallback(env, readData->fileContents);

    // 清理工作
    free(readData->fileContents);
    free(readData);
}

// 这个函数会被JavaScript代码调用以启动异步文件读取
napi_value StartAsyncFileRead(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 获取文件名(JavaScript字符串参数)
    size_t filename_length;
    napi_get_value_string_utf8(env, args[0], NULL, 0, &filename_length);
    char* filename = malloc(filename_length + 1);
    napi_get_value_string_utf8(env, args[0], filename, filename_length + 1, NULL);

    // 设置我们的数据
    FileReadData* readData = malloc(sizeof(FileReadData));
    readData->filename = filename;

    // 创建异步工作
    napi_value async_resource_name;
    napi_create_string_utf8(env, "asyncFileRead", NAPI_AUTO_LENGTH, &async_resource_name);

    napi_async_work work;
    napi_create_async_work(env, NULL, async_resource_name,
                           Execute, Complete, readData, &work);

    // 队列化异步工作
    napi_queue_async_work(env, work);

    return NULL;
}

// 当你加载这个扩展模块时,你需要注册StartAsyncFileRead函数
NAPI_MODULE_INIT() {
    napi_value asyncFileReadFunction;
    napi_create_function(env, NULL, 0, StartAsyncFileRead, NULL, &asyncFileReadFunction);

    napi_set_named_property(env, exports, "startAsyncFileRead", asyncFileReadFunction);
    return exports;
}

在这个例子中,JavaScript 代码可以调用startAsyncFileRead函数,并传递一个文件名,然后这个模块将开始异步地读取文件内容,而不会阻塞其他 JavaScript 代码的执行。文件读取完毕后,会调用 Complete 函数,然后你可以在这里处理读取到的数据,例如,触发一个 JavaScript 事件,或者调用一个回调函数。

通过使用napi_create_async_work,你可以创建强大的 Node.js 扩展,能够执行复杂的、耗时的任务,同时保持应用程序的响应性。

napi_delete_async_workopen in new window

在 Node.js 中,N-API 是一个与 JavaScript 运行时和引擎无关的 API 层,它允许你创建原生插件。使用 N-API,你可以编写用 C 或 C++ 编写的代码,这些代码可以直接与 Node.js 交互。

napi_delete_async_work 是 N-API 中的一个函数,它用于删除以前创建的异步工作。在解释 napi_delete_async_work 之前,我们需要先理解几个基本概念:

  1. 异步编程:在 Node.js 中,很多操作都是异步进行的,比如文件读写、网络请求等,这意味着它们不会立即完成,而是在未来的某个时间点完成。这样可以避免阻塞主线程,提高程序的效率。

  2. 异步工作:在使用 N-API 创建原生插件时,你可能需要执行一些耗时的任务,比如访问数据库或者大量数据的处理。这些任务通常在后台的线程上异步执行,以避免阻塞 Node.js 的主事件循环。

  3. napi_create_async_work:这是一个用于创建异步工作的函数。当你有一个耗时的任务时,你可以使用这个函数将任务放到后台线程去执行,同时你需要传递一些回调函数,它们定义了任务开始、执行和完成时应该做什么。

现在让我们来看看 napi_delete_async_work

napi_status napi_delete_async_work(napi_env env,
                                   napi_async_work work);

napi_delete_async_work 的作用是删除通过 napi_create_async_work 创建出来的异步工作对象。当你的异步任务完成并且相关的回调被调用之后,你应该使用 napi_delete_async_work 来清理内存,防止内存泄漏。

参数说明:

  • env:这是代表 N-API 环境的句柄,每个 N-API 函数都会用到它,它贯穿整个 N-API 使用周期。
  • work:这是你希望删除的异步工作对象的句柄,它是之前通过 napi_create_async_work 创建出来的。

例子:

假设你正在编写一个原生插件,其中有一个函数需要做大量的计算,你想要将这部分工作移到后台线程去执行,以下是一个简化版的示例:

##include `<`node_api.h>

// 声明一个执行大量计算的函数
void ExecuteWork(napi_env env, void* data) {
    // ... 在这里执行计算 ...
}

// 声明一个当工作完成时会被调用的函数
void WorkComplete(napi_env env, napi_status status, void* data) {
    // 获取异步工作句柄
    napi_async_work work = (napi_async_work)data;

    // 删除异步工作对象以释放资源
    napi_delete_async_work(env, work);

    // ... 这里还可以发送结果给JavaScript ...
}

// 接下来是暴露给 JavaScript 的函数,它会启动异步工作
napi_value StartAsyncWork(napi_env env, napi_callback_info info) {
    napi_value work_name;
    napi_async_work work;

    // 创建一个 JavaScript 字符串作为工作名称
    napi_create_string_utf8(env, "Heavy computation", NAPI_AUTO_LENGTH, &work_name);

    // 创建异步工作对象
    napi_create_async_work(env, NULL, work_name, ExecuteWork, WorkComplete, NULL, &work);

    // 将工作排入队列
    napi_queue_async_work(env, work);

    // 返回 undefined 给 JavaScript
    napi_value result;
    napi_get_undefined(env, &result);
    return result;
}

在上面的例子中,StartAsyncWork 是暴露给 JavaScript 的函数,它创建了一个名为 "Heavy computation" 的异步工作,并将其添加到队列中。ExecuteWork 是在后台线程上实际执行工作的函数,而 WorkComplete 则是工作完成后的回调函数,在这个回调中我们使用 napi_delete_async_work 来删除异步工作对象,释放资源。

这样,即使在执行大量的计算时,我们也不会阻塞 Node.js 的主事件循环,从而保证了应用程序的响应性能。

napi_queue_async_workopen in new window

当然可以,首先让我们了解一下 Node.js 是什么。

Node.js 是一个跨平台的 JavaScript 运行环境,它能让开发者使用 JavaScript 来编写服务器端的代码。Node.js 使用非阻塞、事件驱动的模型,这使得它非常适合构建高效、可扩展的网络应用程序。

现在我们来谈谈 N-API 和 napi_queue_async_work

N-API 是 Node.js 的一个 C API,它允许 native addons 与 Node.js 进行交互。native addons 是用 C 或 C++ 编写的模块,可以通过 Node.js 的 N-API 被直接集成到 Node.js 应用程序中。通过这样做,你可以在 Node.js 中执行那些 JavaScript 不擅长的 CPU 密集型任务,同时避免 Node.js 在执行这些任务时被阻塞。

napi_queue_async_work 是 N-API 提供的一个函数,它允许你将异步工作(如文件读写、数据库操作或其他需要大量计算的任务)放入一个队列中,然后在后台线程中执行,从而不会阻塞主 JavaScript 线程。当这个工作完成时,它会将一个回调函数放回到 JavaScript 的事件循环中,从而让你能够处理结果。

举一个例子:

假设你正在编写一个 Node.js 应用程序,该程序需要处理图像,比如转换图片格式,这是一个 CPU 密集型任务。如果你在 Node.js 主线程中进行图像处理,那么在处理期间,你的服务器就无法响应任何其他请求,因为事件循环被阻塞了。这显然不是一个好的情况。

为了解决这个问题,你可以编写一个 native addon,并使用 napi_queue_async_work 将图像处理任务放到后台线程中去执行。这样,主线程就可以继续处理其他请求,而图像处理在后台进行。处理完成后,通过设置的回调函数,你的 JavaScript 代码可以获取到通知,并对处理结果进行相应的操作。

下面是使用 napi_queue_async_work 的一个简化流程:

  1. 定义你需要在后台执行的任务。
  2. 创建一个 napi_async_work 句柄,它代表了这个任务。
  3. 使用 napi_queue_async_work 将任务加入队列并开始执行。
  4. 当任务完成时,Node.js 会自动将一个回调事件放入事件循环中,让你可以在 JavaScript 层面处理结果。

值得注意的是,通常开发者并不需要直接使用 N-API 和 napi_queue_async_work 函数,除非他们正在编写 native addons。对于大多数情况,Node.js 核心 API 或者通过 npm 安装的模块已经足够用于处理各种异步任务。

napi_cancel_async_workopen in new window

好的,Node.js 中的 N-API(Node.js API)是一个用来编写本地插件的 API。这些本地插件是用 C 或 C++等语言编写的,它们能够直接与 Node.js 的运行时交互。N-API 旨在提供一个独立于 JavaScript 引擎的抽象层,这意味着编写的代码不依赖于特定的版本的 Node.js 或 V8 引擎,可以更稳定地跨版本工作。

napi_cancel_async_work 是 N-API 中的一个函数,用于取消之前通过napi_create_async_work创建并通过napi_queue_async_work排队执行的异步任务。实际上,当你需要停止一个还没有开始或者正在执行但是希望提前终止的异步操作时,你可以调用这个函数。

举个简单的例子:

假设你创建了一个本地插件,该插件中有一个长时间运行的计算任务,但是在某些条件下,比如用户取消操作,或者超出一定的等待时间后,你想要取消这个任务。

首先,你会创建一个异步工作项:

napi_value async_work;
napi_create_async_work(env, NULL, resource_name, execute, complete, data, &async_work);

其中,execute 是在异步线程池中运行的函数,而 complete 是在主线程中运行的,data 是传递给它们的数据。

然后,你将这个工作项加入到异步队列中:

napi_queue_async_work(env, async_work);

现在假设你的应用程序遇到了某种情况,需要取消这个异步任务。这时候你就可以调用 napi_cancel_async_work

napi_cancel_async_work(env, async_work);

这将尝试取消排队的工作。如果工作已经开始执行,那么它会在下一个可能的机会点被取消。取消成功后,complete 回调仍然会被调用,你可以在这个回调中清理资源,但是 execute 函数的影响将不会生效。

这里需要注意,取消一个异步工作并不保证能立刻停止它,因为如果该工作已经开始,在它的执行过程中没有检查取消状态,则它将继续运行直到完成。只有在工作支持取消操作,并且正确地检查了异步工作的取消状态时,它才会被真正地取消掉。

使用 napi_cancel_async_work 提供了一种机制来处理那些需要长时间运行的操作,同时也给予了开发者控制权,以在必要时提前终止这些操作,这对于创建响应式良好的应用程序非常重要。

Custom asynchronous operationsopen in new window

Node.js 的 N-API(Native API)是一个用于构建原生插件的接口。原生插件是用 C 或 C++编写的模块,可以直接调用 Node.js 底层 API。N-API 旨在隔离插件与 Node.js 引擎的变动,使得编写的插件可以无需修改就能适应新版本的 Node.js。

在 Node.js v21.7.1 中,“Custom asynchronous operations”指的是通过 N-API 创建自定义的异步操作。这允许你执行可能需要很长时间完成的任务(比如读取大文件、进行密集计算等),而不会阻塞 Node.js 的事件循环。简单来说,它可以帮助你提高应用程序的性能和响应速度,因为主线程可以继续处理其他事件,而不必等待耗时操作完成。

实际应用例子

假设我们要开发一个原生 Node.js 插件,该插件需要执行一个耗时的图像处理操作,例如将图片转换为黑白色。通常,这种操作如果在 Node.js 的主线程上同步执行,会导致应用程序的所有其他活动暂停,包括响应用户请求。

使用 N-API 创建的自定义异步操作可以将图像处理工作移至单独的线程中。以下是实现该功能的高级步骤:

  1. 定义异步工作结构:使用napi_create_async_work函数创建异步工作请求,并定义要在后台执行的函数。

  2. 启动异步工作:通过napi_queue_async_work函数将异步工作添加到队列中,这样它就会在单独的线程上运行。

  3. 完成处理:一旦后台操作完成,会调用一个完成回调函数,在这个函数中你可以将结果传回 JavaScript 代码。

下面是一个伪代码示例,展示了这些步骤如何结合起来:

##include `<`node_api.h>

// 图像处理函数,运行在单独的线程上
void ProcessImageInBackground(napi_env env, void* data) {
    // 这里进行耗时的图像处理操作...
}

// 完成回调函数,运行在主线程上,负责返回结果给JavaScript
void OnImageProcessed(napi_env env, napi_status status, void* data) {
    // 通知JavaScript图像处理已完成...
}

// JavaScript调用的函数,启动异步图像处理
napi_value ConvertImageToBlackAndWhite(napi_env env, napi_callback_info info) {
    // 创建异步工作请求...
    napi_value work_name;
    napi_create_string_utf8(env, "ConvertImage", NAPI_AUTO_LENGTH, &work_name);

    napi_async_work work;
    napi_create_async_work(env, NULL, work_name,
                           ProcessImageInBackground,
                           OnImageProcessed, NULL, &work);

    // 将工作加入队列...
    napi_queue_async_work(env, work);

    // 返回undefined,表示函数已非阻塞方式调用
    return NULL;
}

// 模块初始化代码
NAPI_MODULE_INIT() {
    napi_value convert_fn;
    napi_create_function(env, NULL, 0, ConvertImageToBlackAndWhite, NULL, &convert_fn);

    napi_set_named_property(env, exports, "convertImageToBlackAndWhite", convert_fn);
    return exports;
}

在这段代码中:

  • ProcessImageInBackground 是在后台执行的耗时操作。
  • OnImageProcessed 是异步操作完成后调用的回调函数。
  • ConvertImageToBlackAndWhite 是从 JavaScript 代码中调用以启动整个异步过程的函数。

虽然这只是一个大概的框架,它说明了如何使用 Node.js N-API 创建自定义的异步操作。对于编程新手来说,理解这些概念可能需要时间,但这是 Node.js 高性能原生扩展编程的基础知识之一。

napi_async_initopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。它提供了一些函数,可以让你在 C 或 C++代码中使用 JavaScript 功能。napi_async_init是 N-API 里的一个函数,用来初始化异步操作。

当你想在 Node.js 的原生插件中执行异步任务时,比如读取文件、网络请求或者一些长时间运行的计算,你需要通过正确的方式告诉 Node.js 的事件循环这个任务是异步的。这样做可以让 Node.js 的主线程继续处理其他事件,比如用户的输入、服务器的请求等,而不会因为等待某一个异步任务完成而阻塞。

现在,让我们详细解释一下napi_async_init

napi_async_init

语法:

napi_status napi_async_init(napi_env env,
                            napi_value async_resource,
                            napi_value async_resource_name,
                            napi_async_context* result);
  • env: 这是表示 N-API 环境的句柄。每次 N-API 函数被调用时,都会传递这个环境句柄。
  • async_resource: 这通常是一个 JavaScript 对象,它与异步操作相关联。这个对象在异步操作期间保持活动状态,可以用来存储信息。
  • async_resource_name: 这是一个 JavaScript 字符串,它给出了async_resource的名称,用于调试。
  • result: 这是一个指向异步上下文句柄的指针,在调用napi_async_init后,这个句柄可以用于其他异步操作 API。

使用场景和例子

假设你正在编写一个 Node.js 的原生插件,你需要从一个硬件设备中异步读取数据。你可能会写一个 C 函数来启动读取过程,并且当读取完毕时,你希望能够通知 Node.js。

  1. 首先,你使用napi_async_init来初始化一个异步上下文。这个上下文将用于稍后在异步操作完成时通知 Node.js。

  2. 接着,你开启读取数据的硬件操作,这部分通常涉及向硬件发送命令,然后等待硬件的回应。

  3. 当硬件有数据返回时,你将使用另一个 N-API 函数(比如napi_make_callback)来将数据传回给 Node.js,同时利用之前创建的异步上下文。

  4. 最后,当所有异步操作都完成时,你应该使用napi_async_destroy来清理你创建的异步上下文,避免内存泄漏。

以下是一个简化的示例代码片段,展示如何使用napi_async_init

##include `<`node_api.h>

// 假设有一个异步操作函数
void read_data_from_hardware(/* 参数 */) {
    // ... 与硬件交互的代码 ...
}

napi_value InitiateAsyncOperation(napi_env env, napi_callback_info info) {
    napi_status status;
    napi_async_context context;

    // 初始化异步上下文
    status = napi_async_init(env, NULL, NULL, &context);
    // 检查是否成功
    if (status != napi_ok) {
        // 处理错误情况
    }

    // 启动硬件读取操作,此操作将在未来的某个时刻完成
    read_data_from_hardware(/* 参数 */);

    // ... 其他代码 ...

    // 返回结果给JavaScript
    napi_value result;
    status = napi_get_undefined(env, &result);
    if (status != napi_ok) {
        // 处理错误
    }
    return result;
}

// 注册这个函数到Node.js
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, InitiateAsyncOperation, NULL, &fn);
    napi_set_named_property(env, exports, "initiateAsyncOperation", fn);
    return exports;
}

请注意,上述代码只是一个概念性展示,实际的异步操作需要使用事件循环和回调函数来正确地进行。

napi_async_destroyopen in new window

napi_async_destroy 是 Node.js 中的一个函数,属于 N-API(Node API)的一部分。N-API 是一套 C 语言的 API,它允许你创建可以与 Node.js JavaScript 运行时交互的本地插件。这些本地插件通常是用 C 或 C++ 编写的,目的是为了提供一些 JavaScript 难以实现或者性能不足的功能。

napi_async_destroy 的作用

在解释 napi_async_destroy 之前,我们需要理解其中涉及的几个概念:

  1. 异步操作:在 Node.js 中,很多操作都是异步的,比如文件读写、网络请求等。这意味着这些操作会在后台执行,执行完毕后通过回调函数来通知结果,从而不会阻塞主线程。

  2. async_hooks:这是 Node.js 中用来追踪异步资源生命周期的模块。每当创建一个异步操作时,就会创建一个新的异步资源,并且这个资源会有一个唯一的标识。

  3. napi_async_context:这是 N-API 中的一个结构体,代表了与特定异步操作相关联的上下文信息。当你使用 N-API 创建一个异步操作时,你需要创建这样一个上下文。

现在来看 napi_async_destroy,这个函数的作用是销毁一个已经不再需要的 napi_async_context。当你创建了一个 napi_async_context 后,在异步操作完成并且不再需要这个上下文时,你应该调用 napi_async_destroy 来清理它。如果不这样做,可能会造成内存泄漏。

使用场景举例

假设你正在编写一个本地插件,该插件需要在后台进行一些耗时的计算,并在计算完成后将结果返回给 JavaScript。你可能会这样做:

  1. 在插件初始化时,创建一个 napi_async_context
napi_status status;
napi_async_context async_context;
status = napi_async_init(env, NULL, "example_async_context", &async_context);
  1. 在开始异步操作时,传递这个上下文到一个异步工作队列中。

  2. 当异步操作完成时,使用这个上下文来触发回调。

  3. 在插件被清理或者不再需要处理异步操作时,销毁这个上下文。

napi_status status;
status = napi_async_destroy(env, async_context);

通过这样的方式,你确保了即使是在 C/C++ 层面进行异步操作,也能够正确地管理资源和内存,并且能够和 JavaScript 的事件循环无缝集成。

记住,napi_async_destroy 应该在确保所有使用了这个上下文的异步操作都已经完成之后调用,否则可能会导致无法预料的行为。

napi_make_callbackopen in new window

Node.js 的 N-API 是一个用于构建本机插件(native addons)的 API 层,它允许你使用 C 或 C++ 代码与 Node.js 运行时交互。这个接口设计得足够稳定,使得对 Node.js 版本的升级不会破坏使用 N-API 编写的模块。

napi_make_callback 是 N-API 的一部分,它是一个函数,被用来从原生代码(C/C++ 插件)中调用 JavaScript 函数,并且正确地处理 Node.js 的事件循环和异步操作。

在详细解释 napi_make_callback 之前,需要了解两个概念:Node.js 事件循环和回调函数。

  1. 事件循环: Node.js 使用事件驱动模型来处理并发,其中主要组成部分就是事件循环。事件循环负责监听事件(例如网络请求、文件读写完成等),并将对应的回调函数排队进行执行。

  2. 回调函数: 在 JavaScript 中,一个回调函数是传递给其他函数作为参数的函数,然后在某个事件或条件下被调用。Node.js 大量依赖回调函数来处理非阻塞的异步操作。

napi_make_callback 具体做什么?

当你在本机插件中执行一些异步操作,并希望在这些操作完成时调用 JavaScript 中定义的回调函数,可以使用 napi_make_callback。它确保回调函数的调用遵循 Node.js 事件循环的规则,尤其是在处理嵌套调用或多个异步事件时。

如何使用 napi_make_callback? 下面是一个简化的例子:

假设你在 C++ 插件中有这样一个场景,你想在读取完文件后执行 JavaScript 中的回调函数,你可能会这样写:

void OnFileReadComplete(napi_env env, napi_status status, void* data) {
    // 构造一个JavaScript函数的回调
    napi_value callback;
    napi_value global;
    napi_get_global(env, &global);
    napi_get_reference_value(env, (napi_ref)data, &callback);

    // 假设我们想要传递一个字符串 'Hello world!' 给回调
    napi_value argv[1];
    napi_create_string_utf8(env, "Hello world!", NAPI_AUTO_LENGTH, &argv[0]);

    // 调用JavaScript的回调函数,同时遵循事件循环的规则
    napi_value result;
    napi_make_callback(env, nullptr, global, callback, 1, argv, &result);
    // ...省略清理引用和错误处理的代码...
}

上述代码中的 OnFileReadComplete 函数是在文件读取完毕之后被调用的,它准备好调用在 JavaScript 中提供的回调函数,并通过 napi_make_callback 安全地调用该 JavaScript 函数。env 参数是一个表示 N-API 环境的句柄,status 表示之前的操作状态,而 data 指向预先存储的用户数据(在这个例子中,可能是一个指向 JavaScript 回调函数引用的指针)。

napi_get_global 获取 JavaScript 全局对象的引用,napi_get_reference_value 根据之前创建的引用获取实际的 JavaScript 函数对象。然后使用 napi_create_string_utf8 创建一个 JavaScript 字符串作为参数。最后,napi_make_callback 实际调用 JavaScript 函数。

总结一下,napi_make_callback 在 Node.js 的 N-API 中是一个高级函数,用于在本地代码中按照 Node.js 的事件循环规则调用 JavaScript 函数。通过使用这个函数,开发者能够编写出更安全、更稳定的本机插件,确保与 JavaScript 代码的交互不会违背 Node.js 的设计原则。

napi_open_callback_scopeopen in new window

Node.js 中的 napi_open_callback_scope 是一个函数,属于 Node.js 的 N-API。N-API 是一组稳定的应用程序编程接口(API),允许你创建原生插件。原生插件是用 C 或 C++等语言编写的动态链接库(DLL),可以直接调用 Node.js 运行时和 V8 引擎的功能,通常用于执行特定硬件操作、处理大量数据或其他需要高性能的场景。

在使用 N-API 编写原生代码时,有时你需要在 JavaScript 环境外部创建一个回调,这就是napi_open_callback_scope发挥作用的地方。这个函数帮助你开启一个新的“回调作用域”(callback scope),它允许你在非 JavaScript 线程中安全地调用 JavaScript 函数。

实际上,当你想要从一个原生线程调用一个 JavaScript 函数时,你不能直接这样做,因为 JavaScript 是单线程的,且运行在 V8 引擎的主线程中。因此,你需要通过napi_open_callback_scope来确保你的调用在正确的上下文中执行。

现在,让我们看几个例子:

  1. 异步操作完成后的回调:假设你正在编写一个文件读取模块。你可能会用 C++进行文件的读取操作,并利用 N-API 暴露这个功能给 Node.js。一旦文件读取完毕,你想要运行一个 JavaScript 回调函数来处理读取到的数据。在这种情况下,就需要用到napi_open_callback_scope

  2. 自定义事件发射器:如果你的原生模块像硬件设备那样工作,可能需要在某些事件发生时触发 JavaScript 侧的监听器。例如,在一个温度计模块中,每当检测到温度变化时,你可能会调用一个 JavaScript 函数来处理这个事件。为此,你也需要创建一个新的回调作用域。

  3. 定时器回调:如果你的原生模块暴露了一个定时器功能,JavaScript 代码可以提供一个回调函数,在定时器触发时执行。原生代码部分将使用napi_open_callback_scope来确保当定时器在原生线程触发时,回调函数能够正确地在 JavaScript 环境中执行。

简而言之,napi_open_callback_scope是与napi_close_callback_scope结合使用的,它们包裹你需要在原生线程中执行的 JavaScript 回调函数,确保所有与执行该回调相关的资源被正确管理,并且调用是在合适的上下文中进行的。这对维护 Node.js 应用程序的稳定性和性能至关重要。

napi_close_callback_scopeopen in new window

好的,我会直接进入主题。

首先,让我们理解一下 N-API 是什么。N-API 是一个 Node.js 的 API 层,它允许你使用 C 或 C++编写可以与 JavaScript 代码交互的原生插件。通过使用 N-API,你可以创建性能更高且不会随 Node.js 版本更新而改变的扩展。这对于想要在 Node.js 中利用现有 C/C++库的开发者来说非常有帮助。

当我们提到napi_close_callback_scope,我们正在讨论 N-API 的一个功能,它涉及到所谓的“回调作用域”。当你在原生插件中调用 JavaScript 函数或传递数据时,Node.js 使用“回调作用域”来跟踪和处理这些调用。每次从原生代码调用 JavaScript 代码时,都会创建一个新的回调作用域。

现在,来谈谈napi_close_callback_scope函数本身。这个函数的作用是结束当前的回调作用域。当你打开一个作用域来执行一些操作,并在所有操作完成后,你需要关闭这个作用域,以确保资源得到正确的释放。这就像在编程中打开了一个文件进行读写操作,之后必须关闭它,防止资源泄露或其他潜在问题。

在 N-API 中,开始一个新的回调作用域你会使用napi_open_callback_scope,并在所有操作完成后,使用napi_close_callback_scope来关闭它。

举个例子,假设你有一个 C++函数,它将在某些条件下调用一个 JavaScript 回调函数。在这个场景下,你可能会这样做:

napi_status status;
napi_callback_scope scope;

// 假设env是你当前的napi环境,cb为JavaScript回调函数
status = napi_open_callback_scope(env, cb, &scope);
if (status != napi_ok) {
    // 处理错误...
}

// 调用JavaScript中的回调函数
// ...

// 完成所有的调用后,你需要关闭这个作用域
status = napi_close_callback_scope(env, scope);
if (status != napi_ok) {
    // 处理错误...
}

注意,这段代码并未展示完整的实现细节,比如如何创建napi_envnapi_value等,但它给出了如何在一个简单情况下使用napi_close_callback_scope来结束回调作用域的概念。

总结,napi_close_callback_scope是 N-API 的一部分,用于确保当你完成与 JavaScript 的交互后,任何相关的资源都能被妥善管理和释放。这有助于避免内存泄漏,确保 Node.js 应用程序的稳定性和效率。在实际应用中,任何时候你通过 N-API 打开回调作用域,最终都应当关闭它,以保持良好的资源管理。

Version managementopen in new window

Node.js 中的 N-API 是一个用于构建原生插件(native addons)的 API。原生插件是直接与 Node.js 运行时进行交互的,用 C 或 C++ 编写的模块。N-API 的目的是保证不同版本的 Node.js 之间原生插件的兼容性。

在 Node.js v21.7.1 的文档中,“Version management”通常指的是 N-API 版本管理。每当 Node.js 发布新版本时,N-API 都可能引入新的功能或者变更,这就产生了不同的 N-API 版本。为了保持向后兼容性,并让插件开发者可以针对特定版本编写代码,N-API 提供了版本管理机制。

实际运用示例:

  1. 编写兼容多个 Node.js 版本的原生插件: 假设你希望创建一个原生插件,用于提高图像处理的性能。你会用 C++ 编写处理图像的逻辑,并通过 N-API 将这些功能暴露给 Node.js 代码。在编写插件时,你可以指定你的插件兼容哪些 N-API 版本。例如:

    ##include `<`node_api.h>
    
    // ...你的 C++ 代码...
    
    NAPI_MODULE_INIT() {
      napi_value result;
      napi_create_string_utf8(env, "Hello World!", NAPI_AUTO_LENGTH, &result);
      return result;
    }
    

    package.json 文件中,你可以指明 N-API 的版本:

    {
      "name": "awesome-native-addon",
      "version": "1.0.0",
      "main": "index.js",
      "dependencies": {
        "node-addon-api": "^3.2.1"
      },
      "gypfile": true,
      "binary": {
        "napi_versions": [3, 4, 5]
      }
    }
    

    这表明你的原生插件是与 N-API 版本 3、4 和 5 兼容的。

  2. 检查运行时的 N-API 版本: 当你的 Node.js 应用启动并加载原生插件时,该插件可以检查当前环境下的 N-API 版本是否符合其兼容性需求。如果不符合,你可以选择输出错误消息或者采取其他措施。

    ##include `<`node_api.h>
    
    napi_value Init(napi_env env, napi_value exports) {
      // 检查当前环境中支持的 N-API 版本
      uint32_t napi_version = 0;
      napi_get_version(env, &napi_version);
    
      if (napi_version `<` 3) {
        // 如果 N-API 版本小于3,抛出错误
        napi_throw_error(env, NULL, "N-API version 3 or higher is required for this addon");
      }
    
      // ...初始化插件...
    
      return exports;
    }
    
    NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
    

这样的版本管理使得即使 Node.js 更新了,只要不超出你声明的 N-API 版本范围,你的原生插件仍应该能够正常工作,无需修改代码。这大大降低了维护成本,并提高了原生插件的稳定性和可靠性。

总结一下,N-API 的版本管理是 Node.js 提供的一种机制,它旨在确保原生插件跨不同 Node.js 版本的兼容性,而无需频繁地对插件代码进行更新。通过使用这种机制,插件开发者可以更容易地维护和分发他们的代码,并确保最终用户有更加平滑的体验。

napi_get_node_versionopen in new window

好的,我会直接回答你的问题。

Node.js 中的 napi_get_node_version 函数是 N-API 的一部分,N-API 是一个稳定的 Node.js API 层,它允许本地插件的开发者编写能够在不同版本的 Node.js 上运行的代码,而不需要对每个版本重编译。

当你使用 napi_get_node_version 这个函数时,它可以让你获取当前正在运行的 Node.js 的版本信息。这包括主版本号、次版本号和修订号。这对于确保你的本地插件与特定版本的 Node.js 兼容非常有用。

函数原型如下:

napi_status napi_get_node_version(napi_env env,
                                  const napi_node_version** version);
  • env 是一个表示 N-API 环境的句柄。
  • version 是一个指向 napi_node_version 结构体的指针的指针,这个结构体将会被填充为当前 Node.js 版本信息。

napi_node_version 结构体通常定义如下:

typedef struct {
  uint32_t major;
  uint32_t minor;
  uint32_t patch;
  const char* release;
} napi_node_version;
  • major 表示主版本号。
  • minor 表示次版本号。
  • patch 表示修订号。
  • release 是一个字符串,表示预发布版本标签或空字符串。

调用 napi_get_node_version 并处理返回结果的简单示例代码片段可能如下所示(C 语言):

##include `<`node_api.h>
// ... 其他必要的头文件 ...

napi_env env; // 假设已经初始化了 N-API 环境变量env
const napi_node_version* node_version;

napi_status status = napi_get_node_version(env, &node_version);
if (status == napi_ok) {
  printf("Node.js version: %d.%d.%d\n",
    node_version->major,
    node_version->minor,
    node_version->patch);

  if (node_version->release[0]) {
    printf("Pre-release tag: %s\n", node_version->release);
  }
} else {
  // 处理错误情况
}

当你运行这段代码时,它会输出类似 "Node.js version: 14.15.1" 的信息,根据你运行的 Node.js 版本不同,打印出来的版本号也会不同。

在实际应用中,你可以使用这个函数来确定是否某些特定功能在当前运行的 Node.js 版本中可用,或者如果你在开发一个需要与多个 Node.js 版本兼容的本地模块,你可以使用这个函数来适配不同的版本。

napi_get_versionopen in new window

napi_get_version是 Node.js 中 N-API(即 Node API)的一部分,这是一个用来构建原生插件的 API。原生插件通常是用 C 或 C++编写的模块,它们可以直接运行在操作系统层面,因此性能高效。

在了解napi_get_version之前,先简要介绍一下 N-API:

什么是 N-API?

N-API 是 Node.js 的一个稳定特性,允许开发者用 C 或 C++编写与 Node.js 交互的代码,而不必担心 Node.js 和 V8 引擎之间的变动。这提供了一个抽象层,使得编写的原生插件可以跨 Node.js 的不同版本运行,减少了因版本变更而导致的维护负担。

那么napi_get_version是做什么的?

napi_get_version是一个函数,它允许开发者在他们的原生模块中查询正在使用的 N-API 的版本号。使用这个函数可以确保原生模块能够检查它所支持的 N-API 版本是否与当前环境兼容。

如何使用napi_get_version?

假设你正在编写一个原生模块,并且想要确认你支持的最低 N-API 版本是否被当前的 Node.js 环境支持。你可以调用napi_get_version来获取这个信息。

以下是一个简单的示例,展示如何在 C 代码中调用napi_get_version

##include `<`node_api.h>

napi_value Init(napi_env env, napi_value exports) {
  // 此处声明版本号的变量
  uint32_t napi_version;

  // 调用napi_get_version获取当前的N-API版本号
  napi_status status = napi_get_version(env, &napi_version);

  // 检查调用是否成功
  if (status != napi_ok) {
    // 处理错误...
  }

  // 现在可以根据得到的版本号napi_version进行逻辑处理了
  // 比如,检查版本号是否满足你的原生模块所需的最小版本

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的代码中,我们定义了一个名为Init的初始化函数,它将在模块加载时被调用。在这个函数里,我们通过调用napi_get_version获得了当前环境的 N-API 版本并存储在变量napi_version中。然后,我们可以根据这个版本号来决定是否需要执行某些特定的行为。例如,如果我们知道我们的模块需要至少第 3 版的 N-API,我们便可以比较napi_version和数字 3,以确定是否继续或提供错误消息。

这样的版本检查是很重要的,因为它有助于确保你的代码在不同版本的 Node.js 上都能正常工作,从而提高代码的可靠性和稳定性。

Memory managementopen in new window

在 Node.js 中,内存管理是指如何分配、使用和释放内存资源。Node.js v21.7.1 的文档中关于“内存管理”通常会提到 N-API,这是 Node.js 提供给 C 和 C++扩展的一个 API 接口,它允许这些语言编写的代码与 Node.js 交互。

理解内存管理的重要性,在于防止内存泄漏(程序不需要的内存没有被释放掉)和有效地使用内存资源。下面我会用一些例子来解释在 Node.js 中内存管理的一些概念和实践。

1. Buffer 和 TypedArray

在 Node.js 中处理原始二进制数据时会使用Buffer类或者TypedArrayBuffer是 Node.js 特有的全局对象,而TypedArray是 JavaScript 的一部分。比如,当你从网络接收数据或者读取文件时,这些数据通常会被存储在Buffer对象中。

举个例子:

const fs = require("fs");

// 异步读取文件内容到Buffer
fs.readFile("/path/to/file", (err, data) => {
  if (err) throw err;
  console.log(data); // data是一个Buffer对象,包含了文件的二进制数据
});

2. 垃圾回收(Garbage Collection)

JavaScript 和 Node.js 中的内存大多是自动管理的。意味着创建的变量和对象在不再使用后,会由 JavaScript 的垃圾回收器(GC)自动清理。但是,在使用 C/C++编写的原生扩展模块时,需要手动管理内存。

例如,如果你创建了一个原生的对象,Node.js 就不知道何时该清理这个对象占用的内存。因此,N-API 提供了一系列的函数来帮助开发者确保这部分内存得到合适的处理。

3. N-API 中的内存管理函数

对于使用 N-API 的扩展模块,Node.js 提供了一些 API 来帮助开发者管理内存:

  • napi_create_external(): 创建一个 JavaScript 对象,其中包含外部分配的数据。
  • napi_create_external_buffer(): 为一个已经存在的数据创建一个 Buffer 对象。
  • napi_create_external_arraybuffer(): 创建一个 ArrayBuffer,其数据是外部提供的。
  • napi_ref: 创建一个对象的引用以避免它被垃圾回收器回收。
  • napi_unref: 删除之前设置的引用。

假设你正在编写 C++扩展,并希望将一些 C++创建的数据传给 JavaScript,可以使用napi_create_external()。这样,你就能在 JS 中使用这些数据,同时确保在数据不再需要时能够正确地释放内存。

实际应用的例子 - 创建一个外部 Buffer

以下是一个简单的示例,展示如何在一个 N-API 模块中创建外部 Buffer:

##include `<`node_api.h>

// 假设我们有一个外部分配的数据源
char* external_data = (char*)malloc(1024);
size_t external_data_length = 1024;

// 这是一个用来创建外部Buffer的函数
napi_value CreateExternalBuffer(napi_env env, napi_callback_info info) {
  napi_value buffer;

  // 创建一个Buffer,它引用了外部分配的数据
  napi_create_external_buffer(env,
                              external_data_length,
                              external_data,
                              NULL, // 可选的,当Buffer被GC时调用的finalize回调
                              NULL, // finalize回调的hint参数
                              &buffer);

  return buffer;
}

// 在模块初始化时注册这个函数
NAPI_MODULE_INIT() {
  napi_value externalBufferFunction;

  // 将CreateExternalBuffer函数暴露给JavaScript
  napi_create_function(env,
                       "createExternalBuffer",
                       NAPI_AUTO_LENGTH,
                       CreateExternalBuffer,
                       NULL,
                       &externalBufferFunction);

  // 设置导出的属性
  napi_set_named_property(env, exports, "createExternalBuffer", externalBufferFunction);

  return exports;
}

在上面的示例中,我们使用 C 代码创建了一个外部数据源和一个函数CreateExternalBuffer,然后将这个函数暴露给了 JavaScript。当 JavaScript 代码调用这个函数时,它会返回一个 Node.js 的Buffer对象,这个对象引用了那块外部分配的内存。这样就可以在 Node.js 中直接操作这些数据,而不需要复制它们。注意,当你这样做的时候,必须确保在 JavaScript 层面上不再需要这个 Buffer 时,相应的内存也被释放了。

总的来说,内存管理是一项程序员必须关心的话题,尤其是在涉及到本机扩展的时候。正确的内存管理可以避免内存泄漏,提高程序稳定性和性能。

napi_adjust_external_memoryopen in new window

Node.js 中的napi_adjust_external_memory函数是一个用于通知 V8 引擎调整其跟踪的外部内存使用情况的 API。这个函数属于 Node.js 的 N-API,它是一个稳定的 API 层面,允许原生模块的作者编写与 Node.js 版本无关的代码。

首先我们需要了解几个概念:

  1. V8 引擎:这是 Node.js 底层使用的 JavaScript 引擎,负责执行 JavaScript 代码。
  2. N-API:这是 Node.js 提供的一组 C 语言的 API,允许开发者编写本地插件。这些插件可以直接与 Node.js 底层进行交互,性能高效,并且不依赖于 Node.js 版本。
  3. 外部内存:在创建原生插件时,可能会使用到非 V8 管理的内存资源,比如通过 C/C++分配的内存。这部分内存对于 V8 来说是“外部”的,因为 V8 的垃圾回收器无法直接管理这些内存。

现在,来具体看napi_adjust_external_memory

这个函数主要用途是手动告诉 V8,有多少外部内存正在被使用。因为 V8 自己的垃圾回收器并不知道这些外部内存的存在,没有这个信息,V8 可能就不会及时地进行垃圾收集,从而导致 Node.js 进程使用的总内存过大。

napi_adjust_external_memory的使用方法大致如下:

##include `<`node_api.h>

// 在某个函数内部,你已经分配了一些外部内存:
void* buffer = malloc(1024); // 分配了1024字节的内存

// 然后你需要通知V8这1024字节的外部内存已经被使用
int64_t change_in_bytes = 1024;
int64_t adjusted_value;
napi_status status = napi_adjust_external_memory(env, change_in_bytes, &adjusted_value);

if (status != napi_ok) {
  // 处理错误...
}

在上面的示例中:

  • buffer是通过 malloc 分配的堆内存,它在 C/C++的领域,但对 V8 引擎来说是外部的。
  • 通过调用napi_adjust_external_memory,我们告诉 V8 有 1024 字节的外部内存被使用。
  • change_in_bytes参数是我们刚刚分配或者释放的内存量。
  • adjusted_value会返回调整后 V8 当前跟踪的外部内存总量。
  • 我们也检查status变量以确保调用成功。

实际应用示例:

假设你正在编写一个 Node.js 的原生拓展,该拓展需要处理图像。在处理过程中,你可能会分配大量内存来存储图像数据。如果处理多张图像,那么分配的内存将会相当可观。使用napi_adjust_external_memory就可以告诉 V8 引擎这个情况,以便 V8 可以在合适的时候运行垃圾收集器,避免应用程序消耗过多内存。

正确使用这个 API 有助于改善 Node.js 应用的性能和内存管理,特别是在处理大量数据且不希望影响 V8 垃圾回收行为时。

Promisesopen in new window

Node.js 是一个运行在服务器端的 JavaScript 运行环境,它允许你使用 JavaScript 来编写可以处理文件系统、网络通信等后端任务的程序。而 Promises 是 Node.js(以及现代 JavaScript)中用于处理异步操作的一种机制。

在深入解释 Promises 之前,我们需要理解“异步”这个概念。在编程中,“异步操作”指的是不会立即完成的操作,例如读取文件、查询数据库或发起网络请求。因为这些操作可能需要一些时间来完成,所以如果我们同步地(按顺序一步接一步地)等待它们完成,那么程序的其他部分就会被阻塞,无法执行。为了提高效率和用户体验,我们需要异步地处理这些操作。

Promises 就是为了简化异步编程而生的。一个 Promise 是一个代表未来将要结束的事件的对象。它有三种状态:

  1. Pending(进行中):初始状态,既没有被兑现,也没有被拒绝。
  2. Fulfilled(已兑现):意味着操作成功完成。
  3. Rejected(已拒绝):意味着操作失败。

Promise 对象用 .then().catch() 方法来处理这些状态。.then() 接收两个函数作为参数,第一个是当 Promise 状态变为 Fulfilled 时调用的,第二个是当 Promise 状态变为 Rejected 时调用的。.catch() 则是专门用来捕获 Promise 如果处于 Rejected 状态的情况。

下面是一些实际运用的例子:

例子 1:读取文件

const fs = require("fs").promises; // Node.js 文件系统模块的 Promise 版本

// 异步读取一个文件的内容
fs.readFile("example.txt", "utf8")
  .then((data) => {
    console.log(data); // 成功读取后,输出文件内容
  })
  .catch((error) => {
    console.error(error); // 如果读取失败,输出错误信息
  });

在这个例子中,fs.readFile 是一个异步操作,它返回一个 Promise。如果文件读取成功,Promise 变为 Fulfilled 状态,并且 .then() 中的函数将被调用来输出文件内容。如果读取失败,Promise 变为 Rejected 状态,.catch() 中的函数将被调用来处理错误。

例子 2:网络请求

const fetch = require("node-fetch"); // node-fetch 是一个用来发起网络请求的库

fetch("https://api.example.com/data") // 发起 GET 请求到指定的 URL
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json(); // 解析 JSON 数据
  })
  .then((data) => {
    console.log(data); // 处理数据
  })
  .catch((error) => {
    console.error("There has been a problem with your fetch operation:", error);
  });

在上面的例子中,fetch 函数用来发起一个网络请求。它返回一个 Promise,该 Promise 在网络响应后被解决。如果响应状态良好,我们继续解析 JSON 数据,然后在下一个 .then() 块中处理它。如果网络请求失败或者响应状态不是 OK 的,我们通过抛出异常来直接触发 .catch() 块,打印出错信息。

例子 3:链式操作

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

function doSomething() {
  return new Promise((resolve, reject) => {
    // 假设这里是一些异步操作...
    const result = "first value";
    resolve(result); // 操作成功,调用 resolve 并传入结果
    // 如果有什么问题,应该调用 reject
  });
}

function doSomethingElse(value) {
  // 返回新的 Promise,依赖于前一个操作的结果
  return new Promise((resolve, reject) => {
    // 又是一些异步操作...
    const newResult = `new result based on ${value}`;
    resolve(newResult);
  });
}

function doThirdThing(newResult) {
  // ...又是依赖上一步结果的操作...
  return Promise.resolve(`third result based on ${newResult}`);
}

在这个例子中,我们演示了如何用 .then() 方法来串联多个异步操作,每一步都根据上一步的结果来进行操作。最终,我们打印出最后的结果,或者在任何地方遇到错误时捕获并处理错误。

这些例子展示了在 Node.js 中使用 Promises 来处理异步操作的基本方式。由于 Promises 是原生支持的,因此它们与 Node.js 中的许多库和工具相互集成,大大简化了异步编程的复杂性。

napi_create_promiseopen in new window

Node.js 中的 napi_create_promise 是 N-API 的一部分,N-API 是一个用于构建原生插件的 C API。原生插件是使用 C 或 C++ 编写的模块,它们可以直接与 Node.js 运行时进行交互。napi_create_promise 允许原生代码创建一个 Promise 对象,这个对象可以和 JavaScript 中的 Promise 一样使用,以支持异步操作。

在 JavaScript 中,Promises 是处理异步操作的一种方式。当你有一个可能花费些时间执行的任务,比如从网络读取数据或者查询数据库,你通常会用到 Promise。一个 Promise 代表了一个尚未完成但预计将来会完成的动作,并且提供了一种方法来处理其结果。

现在,让我们看看 napi_create_promise 是如何工作的。下面是一个例子说明了在原生插件中如何创建一个 Promise:

##include `<`node_api.h>

napi_value CreatePromise(napi_env env, napi_callback_info info) {
  napi_value promise;
  napi_deferred deferred;

  // 创建一个Promise对象
  napi_status status = napi_create_promise(env, &deferred, &promise);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 在此处执行异步任务...

  // 假设异步任务完成后,我们要解决(resolve)这个Promise
  // 首先创建一个表示成功结果的JavaScript值
  napi_value result;
  status = napi_create_string_utf8(env, "Success", NAPI_AUTO_LENGTH, &result);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 解决这个Promise
  status = napi_resolve_deferred(env, deferred, result);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 返回创建的Promise对象
  return promise;
}

// 注册上面的函数为一个模块导出
NAPI_MODULE_INIT() {
  napi_value export_fn;
  napi_create_function(env, NULL, 0, CreatePromise, NULL, &export_fn);
  napi_set_named_property(env, exports, "createPromise", export_fn);
  return exports;
}

在这个例子中,我们定义了一个 CreatePromise 函数,它首先调用 napi_create_promise 来创建一个新的 Promise 对象和一个与之关联的 napi_deferred 对象。napi_deferred 对象用于稍后解决(fulfill)或拒绝(reject)这个 Promise。

该函数返回创建的 Promise 对象,然后你可以在 JavaScript 代码中调用这个原生函数获取 Promise 对象,并添加 .then().catch().finally() 回调来处理异步结果。

假设原生模块名为 my_native_module,我们可以在 JavaScript 中这样使用它:

const myNativeModule = require("my_native_module");

const promise = myNativeModule.createPromise();

promise
  .then((result) => {
    console.log(result); // 输出: 'Success'
  })
  .catch((error) => {
    console.error(error);
  });

这里的 myNativeModule.createPromise() 调用原生函数 CreatePromise,后者创建并返回一个 Promise。当异步操作完成后,如果成功,则由原生代码通过 napi_resolve_deferred 解决这个 Promise;如果失败,则通过 napi_reject_deferred 拒绝它。

记住,真正的异步操作应该在其他线程或者事件循环中发生,以避免阻塞 Node.js 的主线程。而本例中的异步操作被简化了,只是为了演示如何创建和使用 napi_create_promise

napi_resolve_deferredopen in new window

napi_resolve_deferred 是 Node.js 的 N-API(原生 API)中的一个功能。在 Node.js 中,N-API 提供了一个与 JavaScript 运行时交互的稳定层,允许创建可以跨多个 Node.js 版本运行的本地插件。

首先,要理解 napi_resolve_deferred,我们需要了解什么是 “deferred” 和 “promise”。在 JavaScript 中,Promise 是异步编程的一种很重要的概念,它代表了一个可能现在、也可能将来才会结束的操作,并且这个操作可以有两种结果:成功(resolved)或失败(rejected)。

Deferred 对象是 Promise 的来源,通常用于底层的实现。一个 deferred 包含了一个 promise 以及改变这个 promise 状态的方法(resolve 来表示成功,reject 表示失败)。你可以把 deferred 想象成带有控制按钮的 promise 工厂。

在使用 N-API 创建原生插件时,你可能会遇到这样的情况:你想要执行一些异步的本地操作(比如读取文件、访问数据库等),并且希望把结果传递回 JavaScript 层面。此时,你可以通过创建一个 deferred 对象和相应的 promise,然后当本地操作完成后,使用 napi_resolve_deferred 来解决(resolve)该 deferred 对象,从而使得 JavaScript 层面的 promise 变为 fulfilled 状态,并且将结果传递给任何等待这个 promise 的处理程序。

例子:

假设你正在编写一个原生插件,该插件提供了一个异步函数 doSomethingAsyncNative,该函数在本地执行一些操作,并在结束时返回一个结果。这里展示如何使用 napi_resolve_deferred

##include `<`node_api.h>

// 异步工作结构体,用于传递数据到异步执行函数和完成回调
typedef struct {
    napi_deferred deferred;
    napi_async_work work;
    // 其他需要传递到异步工作中的数据
} AsyncWorkData;

// 异步执行函数,此处为假设函数
void ExecuteAsyncWork(napi_env env, void* data) {
    // 执行一些耗时的操作

    // 假设完成后的结果是一个整数
    int result = 42;

    // 将结果存储在 AsyncWorkData 结构中以供完成回调使用
    ((AsyncWorkData*)data)->result = result;
}

// 完成回调函数
void CompleteAsyncWork(napi_env env, napi_status status, void* data) {
    AsyncWorkData* asyncWorkData = (AsyncWorkData*)data;

    if (status == napi_ok) {
        // 创建一个JavaScript数字以包装结果
        napi_value jsResult;
        napi_create_int32(env, asyncWorkData->result, &jsResult);

        // 解决 deferred,即解决关联的 promise 并传递结果
        napi_resolve_deferred(env, asyncWorkData->deferred, jsResult);
    } else {
        // 如果有错误,拒绝 promise
        napi_reject_deferred(env, asyncWorkData->deferred, NULL);
    }

    // 删除异步工作项
    napi_delete_async_work(env, asyncWorkData->work);
    free(asyncWorkData);
}

// 绑定到 JavaScript 的表示异步操作的函数
napi_value DoSomethingAsyncNative(napi_env env, napi_callback_info info) {
    // 创建一个 deferred 和 promise
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);

    // 分配 AsyncWorkData 结构并初始化
    AsyncWorkData* asyncWorkData = malloc(sizeof(AsyncWorkData));
    asyncWorkData->deferred = deferred;

    // 创建和队列异步工作
    napi_create_async_work(env, NULL, NULL, ExecuteAsyncWork, CompleteAsyncWork, asyncWorkData, &asyncWorkData->work);
    napi_queue_async_work(env, asyncWorkData->work);

    // 返回 promise 给 JavaScript
    return promise;
}

上面的代码展示了如何在原生模块中使用 napi_resolve_deferred。我们定义了一个 DoSomethingAsyncNative 函数,它创建了一个异步任务并立即返回一个 promise。当这个异步任务完成时,它将调用 CompleteAsyncWork 回调函数,在这个回调中,我们最终调用了 napi_resolve_deferred 来解决 promise,如果异步工作成功,则返回结果;如果失败,则拒绝 promise。

请注意,这个例子是对 N-API 使用方式的简化说明,实际使用时还需要考虑错误处理、内存管理等更多因素。

napi_reject_deferredopen in new window

Node.js 中的 napi_reject_deferred 是一个用于 N-API(Native API)的函数,它允许你在编写原生插件时拒绝一个之前创建的延迟对象(Deferred)。首先,我们要理解几个关键概念:

  1. N-API:这是 Node.js 提供的一套 C 语言的 API,允许你编写可以直接与 JavaScript 交互的原生插件。原生插件通常是为了提高性能或者利用 C/C++ 库。

  2. Promise:这是 JavaScript 中的一个对象,代表一个异步操作的最终完成(或失败),以及它的结果值。

  3. Deferred:这是实现 Promise 功能的一种方式。当你创建一个 Deferred 对象时,实际上你获得了控制相应 Promise 解决(resolve)或拒绝(reject)的能力。简单来说,Deferred 具有解决和拒绝两个操作,分别对应 Promise 的成功和失败状态。

现在,让我们谈谈 napi_reject_deferred 函数的作用。此函数用于拒绝一个已经创建的 Deferred 对象,这意味着与该 Deferred 相关联的 Promise 将会以失败结束,并且你可以指定错误原因。

下面是 napi_reject_deferred 函数的定义:

napi_status napi_reject_deferred(napi_env env,
                                 napi_deferred deferred,
                                 napi_value rejection);
  • env:环境句柄,用于表示 Node.js 运行时的上下文。
  • deferred:Deferred 对象的句柄,之前使用 napi_create_promise 创建。
  • rejection:一个 napi_value,表示拒绝的原因,通常是一个 Error 对象。

实践示例

假设你正在编写一个原生模块,它需要执行一个长时间运行的操作,例如从网络下载文件。你希望提供一个函数,返回一个 Promise,在操作完成时解决,如果出现错误则拒绝。

##include `<`node_api.h>

// 模拟的异步操作回调
void download_complete_callback(bool is_success, napi_deferred deferred, napi_env env) {
    if (is_success) {
        // 假设下载成功,我们解决 Promise
        napi_value undefined;
        napi_get_undefined(env, &undefined);
        napi_resolve_deferred(env, deferred, undefined);
    } else {
        // 如果下载失败,我们拒绝 Promise
        napi_value error;
        napi_create_string_utf8(env, "Download failed", NAPI_AUTO_LENGTH, &error);
        napi_reject_deferred(env, deferred, error);
    }
}

// 你的原生模块导出的函数
napi_value start_download(napi_env env, napi_callback_info info) {
    napi_value promise;
    napi_deferred deferred;
    napi_create_promise(env, &deferred, &promise);

    // ... 执行异步下载操作 ...
    // 假设下载完成后会调用 `download_complete_callback`

    return promise; // 返回 Promise 给 JavaScript 调用者
}

在这个例子中,我们通过 N-API 创建了一个 Promise 和一个相关联的 Deferred。然后我们开始一个异步的下载操作,并且在下载完成时调用 download_complete_callback。根据操作成功或者失败,我们调用 napi_resolve_deferred 或者 napi_reject_deferred 来改变 Promise 的状态。

在 JavaScript 中使用时可能像这样:

const nativeModule = require("your-native-module");

nativeModule.start_download().then(
  () => console.log("下载成功"),
  (error) => console.error("下载失败:", error)
);

以上就是 napi_reject_deferred 的基本概念、用法,以及在实际情况中如何使用它来管理原生模块中的异步操作。

napi_is_promiseopen in new window

napi_is_promise 是 Node.js 中的一个 API 函数,属于 N-API。N-API 是一套用 C 或 C++ 编写的原生插件的接口,允许你创建可以在不同版本的 Node.js 中稳定运行的原生模块。

具体来说,napi_is_promise 函数用来检查一个给定的 N-API 值是否是一个 Promise 对象。在 JavaScript 中,Promise 是处理异步操作的常用方式。当你有一个异步任务,如从文件系统读取文件或从网络请求数据时,你可能会使用 Promise 来管理这些异步操作。

下面是对 napi_is_promise 函数的解释:

  1. 函数作用:该函数用来识别一个 N-API 值是否是一个 Promise。如果是,那么你可以对其进行 Promise 相关的操作,比如 then、catch 等。

  2. 参数napi_is_promise 接受两个参数:

    • env:当前执行环境。
    • value:待检查的 N-API 值。
    • is_promise:输出参数,指向布尔值的指针,如果 value 是 Promise,则设置为 true,否则设置为 false
  3. 返回值:返回状态码,通常这些状态码表示操作是否成功。在正常情况下,你会得到 napi_ok 表示没有发生错误。

现在,让我们通过一个实际的例子来说明怎样使用 napi_is_promise。假设你正在编写一个原生模块,并且你想提供一个函数,这个函数接受一个参数,但是你需要验证这个参数是否为 Promise。

##include `<`node_api.h>

// 一个 N-API 的原生函数
napi_value MyFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1];
    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

    // 检查是否传入了至少一个参数
    if (argc `<` 1) {
        napi_throw_type_error(env, NULL, "Function expects a parameter.");
        return NULL;
    }

    // 确认这个参数是否为 Promise
    bool is_promise = false;
    napi_is_promise(env, args[0], &is_promise);

    if (is_promise) {
        // 参数是 Promise,可以执行一些与 Promise 相关的操作
    } else {
        // 参数不是 Promise,抛出异常或者进行其他处理
        napi_throw_type_error(env, NULL, "Expected a Promise.");
    }

    // 返回 undefined
    napi_value undefined;
    napi_get_undefined(env, &undefined);
    return undefined;
}

// 初始化函数,注册 MyFunction
NAPI_MODULE_INIT() {
    napi_value fn;
    napi_create_function(env, NULL, 0, MyFunction, NULL, &fn);
    napi_set_named_property(env, exports, "myFunction", fn);
    return exports;
}

在上面的例子中,我们定义了一个名为 MyFunction 的原生函数,它检查传递给它的第一个参数是否为 Promise,并据此进行相应的处理。

要注意的是,这个代码片段是用 C 语言编写的,如果你是 Node.js 开发者而不是插件作者,你可能不需要直接与这类底层代码打交道。对于 JavaScript 开发者来说,Node.js 引擎已经内置了对 Promises 的支持,您通常会直接使用 JavaScript 的 Promise 类来处理异步流程。

Script executionopen in new window

Node.js 是一个基于 Chrome V8 JavaScript 引擎的 JavaScript 运行环境。它允许你在服务器端运行 JavaScript 代码,这意味着你可以用 JavaScript 写程序来处理网站后台逻辑、数据库操作,以及构建可扩展的网络应用等。

在 Node.js 的文档中,"Script execution" 指的是如何在 Node.js 环境中执行 JavaScript 脚本。这里特别涉及 N-API(Node.js API),它是一个为了构建原生插件而设计的稳定的 API 层。N-API 允许开发者不必担心底层 JavaScript 引擎的变化而影响他们的模块。开发者可以通过使用 N-API 编写与 Node.js 版本无关的代码,从而提升原生模块的兼容性和稳定性。

下面我将用一些实际的例子来解释 Node.js 中的脚本执行:

示例 1: 执行简单脚本

假设你有一个简单的 JavaScript 文件 hello.js,内容如下:

console.log("Hello, World!");

在 Node.js 中执行这个脚本很简单。你只需在终端中运行以下命令:

node hello.js

执行后,你将看到控制台输出 "Hello, World!"。

示例 2: 使用 N-API 执行脚本

N-API 主要用于编写 C 或 C++ 的扩展,然后通过 JavaScript 调用这些扩展。当你需要更高性能或者需要在 Node.js 中使用一些 JavaScript 原生不支持的功能时,N-API 就派上用场。

例如,我们有一个 C++ 函数,用于计算两个数字的和,并希望能在 Node.js 中调用它。首先我们需要使用 N-API 来创建一个包装函数(binding):

#include `<`node.h>

void Add(const v8::FunctionCallbackInfo`<`v8::Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  int sum = args[0].As`<`v8::Number>()->Value() + args[1].As`<`v8::Number>()->Value();
  auto result = v8::Number::New(isolate, sum);
  args.GetReturnValue().Set(result);
}

void Initialize(v8::Local`<`v8::Object> exports) {
  NODE_SET_METHOD(exports, "add", Add);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

然后,在 Node.js 中,我们可以加载并使用这个原生模块:

const nativeAddon = require("./build/Release/addon");
console.log(nativeAddon.add(3, 5)); // 输出:8

上面的示例中,我们首先在 C++ 代码中定义了一个 Add 函数,然后通过 NODE_MODULE 宏将其暴露给 Node.js。在 JavaScript 代码中,我们通过 require 加载编译后的模块文件,并调用 add 方法。

请注意,N-API 相关的代码编写比较低级且复杂,通常只有在性能非常关键或需要使用 Node.js 不直接支持的系统级功能时,才会考虑使用。

以上就是对 Node.js 脚本执行和 N-API 的一个简介和实例说明。希望这对你理解 Node.js 的脚本执行有所帮助!

napi_run_scriptopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者能够在服务器端运行 JavaScript 代码。N-API(Node.js API)是 Node.js 提供的一组 C 语言接口,允许原生插件的编写者不受 Node.js 版本变化的影响,提高其稳定性和维护性。

napi_run_script 是 N-API 的一个函数,它使得原生模块可以执行一个字符串中包含的 JavaScript 代码,并获取执行结果。这个功能类似于在全局作用域中调用 eval() 函数,但是 napi_run_script 是由 N-API 提供的,因此它是在 C 或 C++ 代码中使用的。

下面我会详细解释 napi_run_script 功能,并给出一个如何在实际中使用它的例子。

函数原型

在 Node.js 的 N-API 文档中,napi_run_script 的函数原型是:

napi_status napi_run_script(napi_env env,
                            napi_value script,
                            napi_value* result);
  • env: 这是一个表示 N-API 调用环境的句柄。
  • script: 这是一个包含了要执行的 JavaScript 代码的 N-API 值。
  • result: 这是一个指向 N-API 值的指针,在函数执行成功后,它将指向脚本的返回值。

如何使用

为了使用 napi_run_script,你需要完成下面的步骤:

  1. 编写一个原生模块,其中包含 C/C++ 代码,调用 napi_run_script 函数。
  2. 创建一个 JavaScript 字符串,包含你想要执行的代码。
  3. 将这个字符串转换为 N-API 值(napi_value)。
  4. 调用 napi_run_script 并传入该 N-API 值。
  5. 获取执行的结果,并处理它。

实际例子

假设我们想在一个原生模块中执行一段 JavaScript 代码来计算两数之和并返回结果。

首先,你需要包含 Node.js 的 N-API 头文件,然后编写一个原生函数,比如 ExecuteScript

##include `<`node_api.h>

// 假设 'env' 和 'exports' 已经通过初始化函数获得

napi_value ExecuteScript(napi_env env, const char* code) {
    napi_value script, result;
    napi_status status;

    // 将 C 字符串转换为 N-API 的字符串值
    status = napi_create_string_utf8(env, code, NAPI_AUTO_LENGTH, &script);
    if (status != napi_ok) return nullptr;

    // 执行 JavaScript 代码
    status = napi_run_script(env, script, &result);
    if (status != napi_ok) return nullptr;

    // 返回执行结果
    return result;
}

现在你可以使用这个函数来执行脚本了。例如,下面的代码片段执行了一个简单的相加操作:

// 在原生模块中调用 ExecuteScript
napi_value sum_result = ExecuteScript(env, "1 + 2");
if (sum_result == nullptr) {
    // 错误处理
}

注意:在真实的应用中,你需要检查每次 N-API 调用的返回状态以确保没有错误发生。如果有错误发生,你应该适当地处理它们。

总结起来,napi_run_script 让你能够在 N-API 插件中执行 JavaScript 代码,并且可以在 C/C++ 中处理执行结果。它主要用于那些需要从本地代码中动态运行 JavaScript 代码的场景。

libuv event loopopen in new window

好的,让我来解释一下 Node.js 中的 libuv event loop。

在计算中,事件循环是一个编程构造,它等待并发送消息或事件。Node.js 使用 libuv 库,这是一个跨平台的 C 库,用于处理异步 I/O(输入/输出)操作和实现事件循环。

在 Node.js 中,当你启动一个应用时,Node.js 实例会创建一个事件循环。这个事件循环是用来处理非阻塞的异步操作的,比如:

  • 文件系统操作(读写文件)
  • 网络请求(HTTP, TCP, UDP 等)
  • 定时器(setTimeout, setInterval)
  • 用户自定义事件

实际上,libuv 将这些操作转化为底层的系统调用,并且在操作完成时,它将结果返回给 Node.js ,然后 Node.js 能够继续执行 JavaScript 回调函数。

事件循环主要分为几个阶段:

  1. 定时器阶段 - 这里检查 setTimeout 和 setInterval 回调。
  2. I/O 回调阶段 - 处理某些系统操作的回调,如 TCP 错误。
  3. 空闲、准备阶段 - 在这个阶段内部准备工作。
  4. 轮询阶段 - 检索新的 I/O 事件; 执行与 I/O 相关的回调,几乎所有情况下都会处理。
  5. 检查阶段 - setImmediate() 回调在这里执行。
  6. 关闭回调阶段 - 比如 socket.on('close', ...)。

了解这些概念后,让我们来看一些实际的例子。

例子 1:文件读取

假设你要读取一个大文件。在同步操作中,你的程序会停在那里直到整个文件都被读取。但在 Node.js 中,你可以这样做:

const fs = require("fs");

fs.readFile("/path/to/a-large-file.txt", (err, data) => {
  if (err) throw err;
  console.log(data);
});

fs.readFile 被调用时,Node.js 会将文件读取操作交给 libuv,然后继续执行事件循环的下一个任务。一旦文件读取完成,读取文件的回调就会被加入到事件循环中以供执行。

例子 2:网络请求

考虑这样一个 HTTP 服务器:

const http = require("http");

const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("Hello World\n");
});

server.listen(8080, () => {
  console.log("Server running at http://127.0.0.1:8080/");
});

Node.js 启动 HTTP 服务器并监听端口 8080。每当有 HTTP 请求到达服务器时,事件循环就会运行回调函数,无论何时,只要准备好了,就会向客户端发送 "Hello World" 响应。

例子 3:实时聊天应用

如果你创建一个实时聊天应用,服务器需要同时处理多个客户端的消息。使用 Node.js 和 WebSocket,你可以这样做:

const WebSocket = require("ws");
const wss = new WebSocket.Server({ port: 8080 });

wss.on("connection", function connection(ws) {
  ws.on("message", function incoming(message) {
    console.log("received: %s", message);
  });

  ws.send("something");
});

每当一个新的客户端连接到 WebSocket 服务器时,'connection' 事件就会被触发,并传递一个代表该连接的 ws 对象。然后,你可以在这个对象上监听消息事件。由于 Node.js 是非阻塞的,因此可以同时服务于许多客户端,而不会因为单个操作而卡住。

总之,libuv 事件循环让 Node.js 能够高效地处理异步操作,它使得 Node.js 特别适合 I/O 密集型任务,而不必担心程序性能的问题。

napi_get_uv_event_loopopen in new window

Node.js 中的 N-API 是一个用于构建原生插件的 API。N-API 设计的目标是不依赖于 JavaScript 引擎的版本,这意味着使用 N-API 编写的插件可以在不同版本的 Node.js 上运行,而无需重新编译。

在 Node.js 中,napi_get_uv_event_loop 是一个函数,它允许你从 N-API 插件中访问 Node.js 的底层事件循环。事件循环是 Node.js 异步操作的核心,它负责调度并管理所有异步事件和操作,如文件 I/O、网络请求等。Node.js 使用了 libuv 作为其事件循环的实现,libuv 是一个跨平台的异步 I/O 库。

现在,让我们深入理解 napi_get_uv_event_loop 函数:

用途

当你在创建一个原生模块(也就是 C 或 C++ 编写的扩展)时,可能需要与 Node.js 的事件循环进行交互。通过 napi_get_uv_event_loop 函数,你可以获取指向事件循环的指针,然后在你的原生代码中使用 libuv 提供的各种功能。

示例

假设你正在开发一个原生模块,该模块需要执行一个异步操作,例如读取某个硬件设备的状态。在这种情况下,你可以使用 napi_get_uv_event_loop 来获得事件循环,并使用 libuv 提供的相关函数来创建一个异步请求。

##include `<`node_api.h>
#include `<`uv.h>

// 一个异步工作结构体,用于保存你的数据和回调
typedef struct {
  uv_work_t request; // libuv
  napi_ref callback; // N-API
  // 其他需要的自定义数据...
} my_async_work;

// 这是你的异步工作的执行函数
static void MyAsyncExecute(uv_work_t* req) {
  my_async_work* work = (my_async_work*)req->data;
  // 执行一些耗时的任务,比如读取硬件状态...
}

// 这是完成耗时任务之后的回调函数
static void MyAsyncComplete(napi_env env, napi_status status, void* data) {
  my_async_work* work = (my_async_work*)data;
  // 调用 JavaScript 回调函数,传递结果...
}

// 这是 N-API 暴露给 JavaScript 的函数
napi_value MyFunction(napi_env env, napi_callback_info info) {
  // 获取事件循环
  uv_loop_t* loop;
  napi_get_uv_event_loop(env, &loop);

  // 分配和初始化你的异步工作结构体
  my_async_work* work = (my_async_work*)malloc(sizeof(my_async_work));
  work->request.data = work;

  // 省略:解析 JavaScript 传递的参数,包括回调函数

  // 把你的异步请求排队到事件循环
  uv_queue_work(loop, &work->request, MyAsyncExecute, MyAsyncComplete);

  // 返回一些值给 JavaScript
  napi_value result;
  napi_get_undefined(env, &result);
  return result;
}

在上面的例子中,MyFunction 被暴露为一个可以从 JavaScript 调用的函数。当 JavaScript 代码调用这个函数时,它会初始化一个异步工作请求,并将其排队到 Node.js 的事件循环中。当异步任务执行完成后,MyAsyncComplete 将被调用,以便给 JavaScript 传递结果。

请注意,真实情况下的错误处理和内存管理会更复杂,这里的示例只是为了说明如何使用 napi_get_uv_event_loop

总结起来,napi_get_uv_event_loop 让你的原生模块能够利用 Node.js 的事件循环机制来执行异步任务,使得原生模块可以像 Node.js 中的 JavaScript 代码那样非阻塞地运行。

Asynchronous thread-safe function callsopen in new window

Node.js 是一个基于 JavaScript 的后端平台,它允许你使用 JavaScript 编写服务器端代码。它的优势在于非阻塞性 I/O 模型,这使得它很适合处理高并发情况。

在 Node.js 中,大部分操作都是异步的,比如文件读取、网络请求等,这些操作不会立即完成,所以它们不会阻塞程序的其他部分。然而,在某些情况下,我们可能需要执行 CPU 密集型任务或一些阻塞性操作,如果这些操作在 Node.js 的主线程上执行,就会影响整个应用的性能。

为此,Node.js 提供了n-api来创建本地插件,以便利用 C 或者 C++增强 Node.js 的性能。n-api还允许开发者在不同的线程中运行代码,这样做的好处是可以进行一些复杂的计算而不阻塞主事件循环。

"异步线程安全函数调用(Asynchronous Thread-safe Function Calls)" 是n-api提供的一个特性,它允许原生代码(例如 C/C++模块)在任何线程上安全地调用 JavaScript 函数。这是通过Thread-safe function来实现的。

Thread-safe function

这是一种特殊类型的函数,可以让你从任何线程安全地调用 JavaScript 回调函数。即使你的代码在不同的线程上运行,并且这些线程尝试并发地调用 JavaScript 代码,使用 Thread-safe function 也可以确保不会发生冲突或竞争条件。

实际应用例子

假设你正在编写一个图像处理应用,用户上传图片,你的服务需要对其进行压缩和格式转换。这个处理过程是 CPU 密集型的,如果在 Node.js 的主线程上进行,将会造成服务器响应迟缓。为了解决这个问题,你可以编写一个 C++扩展,使用 N-API 创建一个线程安全的函数。

##include `<`napi.h>

// 工作函数,可能在后台线程上运行
void ProcessImage(const Napi::CallbackInfo& info) {
    // ... 图像处理逻辑 ...

    // 当完成工作时,调用JavaScript的回调函数
    Napi::ThreadSafeFunction tsfn = info[0].As`<`Napi::ThreadSafeFunction>();

    // 通知Node.js线程要调用的回调函数
    napi_status status = tsfn.BlockingCall([](Napi::Env env, Napi::Function jsCallback) {
        // 调用JavaScript层面的回调,并传递结果
        jsCallback.Call({Napi::String::New(env, "处理完成")});
    });

    // 确保释放线程安全函数资源
    tsfn.Release();
}

// 初始化函数,设置导出
Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set(
        Napi::String::New(env, "processImage"),
        Napi::Function::New(env, ProcessImage)
    );
    return exports;
}

NODE_API_MODULE(addon, Init)

在 JavaScript 代码中,你会像这样调用这个扩展:

const addon = require("./build/Release/addon");

function callback(result) {
  console.log(result); // 输出: '处理完成'
}

addon.processImage(callback);

在这个例子中,processImage是一个异步函数,它启动了一个后台线程来处理图像,并且不会阻塞 Node.js 的主事件循环。一旦图像处理完成,它将安全地调用 JavaScript 层面的callback回调函数,即使这个调用发生在另一个线程上。

Calling a thread-safe functionopen in new window

好的,让我们来深入了解 Node.js 中调用线程安全函数的概念。在 Node.js v21.7.1 文档中,关于"Calling a thread-safe function"是指使用 N-API 中提供的一种机制,来从任何线程安全地调用 JavaScript 函数。

首先需要明白几个关键点:

  1. JavaScript 单线程性质:JavaScript 是单线程运行的,这意味着它一次只能执行一个任务。但在现实世界的应用中,我们经常需要执行后台任务(如文件读写、网络请求等),而不阻塞主线程。

  2. Node.js 的异步非阻塞 I/O:Node.js 通过事件循环和回调函数,使得可以非阻塞地处理这些耗时操作。

  3. 多线程:尽管 JavaScript 是单线程的,但 Node.js 可以通过 C++扩展或者新的 Worker Threads 模块创建多线程。

  4. 线程安全:当多个线程尝试同时访问和修改同一数据时,如果没有适当的同步,就会导致竞态条件。线程安全是指代码在多线程环境下执行时,能够正确处理访问共享资源的逻辑,以避免错误和数据损坏。

现在,当我们需要在一个 Node.js 的原生模块(通常是用 C++编写)中的一个工作线程上运行代码,并且想要在某个时刻回到 JavaScript 环境中执行一些 JavaScript 代码时,我们就需要用到线程安全函数。N-API 提供了napi_create_threadsafe_function和相关 APIs 来创建这样一种函数。

让我们通过一个例子来说明这一点:

假设你正在编写一个 Node.js 原生模块,该模块需要执行 CPU 密集型的图像处理任务。因为这个任务可能需要几秒钟,所以你决定将其放在一个单独的工作线程中进行,以免阻塞 Node.js 的主线程。当图像处理完成后,你想通知 JavaScript 代码并发送结果回去。这里就可以使用线程安全函数了。

以下是该过程的简化步骤:

  1. 在你的原生模块中,使用napi_create_threadsafe_function创建一个线程安全函数。你将提供一个 JavaScript 回调函数,在适当的时候,这个线程安全函数将被调用。

  2. 从工作线程中,当你完成了图像处理任务,使用napi_call_threadsafe_function调用这个线程安全函数,并传递处理后的图像数据。

  3. 这个线程安全函数将按照 Node.js 的规则安全地把控制权交还给 JavaScript 环境,并调用 Step 1 中提供的 JavaScript 回调函数,将图像处理结果传递给它。

具体代码可能如下所示:

napi_value MyNativeModuleInit(napi_env env, napi_value exports) {
    // 执行一些初始化工作

    // 创建线程安全函数
    napi_value my_js_cb;
    // 假设my_js_callback_ref是已绑定的JavaScript回调函数的引用
    napi_get_reference_value(env, my_js_callback_ref, &my_js_cb);

    napi_threadsafe_function tsfn;
    napi_create_threadsafe_function(env, my_js_cb, NULL, "MyThreadsafeFunction", 0, 1, NULL, NULL, NULL, MyCallJs, &tsfn);

    // 启动工作线程,传递线程安全函数给它

    return exports;
}

void MyImageProcessingThread(void* data) {
    // ...执行图像处理...

    // 处理完毕,现在调用线程安全函数
    napi_call_threadsafe_function(tsfn, processed_image_data, napi_tsfn_blocking);
}

// 被线程安全函数内部调用以触发JavaScript端的回调
void MyCallJs(napi_env env, napi_value js_cb, void* context, void* data) {
    // 创建一个napi_value来包装data,然后作为参数传递给回调函数js_cb
}

以上代码是一个高级概述,真正的实现将涉及更多细节,比如错误处理、资源清理等。通过使用线程安全函数,原生模块可以在不阻塞 Node.js 主线程的情况下,安全地协调 JavaScript 和 C++代码之间的交互。

Reference counting of thread-safe functionsopen in new window

Node.js 中的 N-API 是一套用于构建原生插件的 API。这些原生插件允许你用 C 或 C++编写代码,然后在 Node.js 环境中运行。这通常用于执行性能密集型任务,如数学计算、图像处理或与系统底层硬件交互。

Reference Counting of Thread-Safe Functions

在 Node.js 中,当我们谈论线程安全函数(Thread-safe functions)时,我们指的是一类特殊的函数,它们可以安全地从多个线程调用,并且能在 Node.js 的事件循环中正确地执行回调。

"Reference counting" 是一种资源管理技术,确保只要有任何地方需要使用一个资源(比如一个对象或者内存空间),该资源就不会被销毁。在资源不再被需要时,通过减少引用计数,最终可以释放它。

对于线程安全函数而言,使用引用计数意味着你可以跟踪有多少活动的异步操作正在使用该函数。每当开始一个新的操作时,增加引用计数;每当操作完成时,减少引用计数。当引用计数降至零时,表示没有任何异步操作正在使用该函数,它可以被安全销毁。

实际应用例子:

假设你正在开发一个 Node.js 应用程序,需要在后台线程中执行一些 CPU 密集型的图像处理任务。为了不阻塞主事件循环,你决定使用线程安全函数来从不同线程发送处理结果回到 Node.js 的主线程。

下面是一个简化的示例:

  1. 创建一个线程安全函数: 你首先创建一个线程安全函数,它可以从其他线程调用,并将处理结果传回主线程。
napi_threadsafe_function tsfn;

void CallJs(napi_env env, napi_value js_callback, void* context, void* data) {
  // 这里可以调用JavaScript的回调函数,处理从其他线程传回的数据
}

// 创建线程安全函数
napi_create_threadsafe_function(env, js_callback, async_resource, async_resource_name, 0, 1, nullptr, nullptr, nullptr, CallJs, &tsfn);

在这段代码中,napi_create_threadsafe_function 就是用来创建线程安全函数的 API 调用,其中 CallJs 是当其他线程准备好数据时将被调用的 C++函数。

  1. 增加引用计数: 当你开始一个新的图像处理操作时,你会增加线程安全函数的引用计数。
napi_acquire_threadsafe_function(tsfn);
  1. 在其他线程上处理图像并将结果传回: 在后台线程中进行图像处理,处理完后使用线程安全函数将结果传回主线程。
void ProcessImageInBackground(std::string imagePath) {
  // 执行图像处理...
  // 处理完成后,将结果通过线程安全函数发送回主线程
  napi_call_threadsafe_function(tsfn, resultData, napi_tsfn_blocking);
}

std::thread worker(ProcessImageInBackground, "path/to/image.png");
  1. 减少引用计数: 当后台任务完成并且结果已经被传递回主线程后,你减少线程安全函数的引用计数。如果减少后计数为零,函数资源将被清理。
napi_release_threadsafe_function(tsfn, napi_tsfn_release);
  1. 在 JavaScript 中接收结果: 在 Node.js 的 JavaScript 部分,你定义了一个回调函数来接收处理结果。
function onImageProcessed(result) {
  console.log("Image processed:", result);
}

以上步骤演示了一个使用线程安全函数和引用计数机制的简单流程。这样可以确保在异步操作完成前,函数资源不会被错误地销毁。

Deciding whether to keep the process runningopen in new window

Node.js 中的 N-API 是一个用于构建本地插件的 API。它允许你用 C 或 C++ 编写与 Node.js 交互的代码,这些代码可以直接调用 JavaScript 引擎提供的功能,并且可以创建或操作 JavaScript 值。

在使用 N-API 构建本地插件时,有时候需要决定是否让 Node.js 进程继续运行,即使 JavaScript 没有活动任务要执行。例如,如果你的本地插件启动了一个后台线程或者其他异步操作,可能希望保持 Node.js 进程运行直到这些操作完成。

为了控制这一行为,N-API 提供了两个重要的函数:

  1. napi_ref_threadsafe_function
  2. napi_unref_threadsafe_function

通过这些函数,你可以向 Node.js 引擎表明,尽管当前没有 JavaScript 代码在运行,但是后台仍有活动正在进行,因此不应该退出进程。

下面通过几个例子来说明这些函数的实际使用情况:

例子 1:启动后台任务

假设你有一个执行长时间计算的 C 函数,你不想阻塞 Node.js 的事件循环。你可能会在本地插件中创建一个线程来处理这个计算。

##include `<`node_api.h>
#include `<`stdlib.h>

// 这个函数将在新线程上执行的长时间计算任务
void LongComputationTask(void* data) {
    // ... 长时间计算 ...
    // 完成后通知 Node.js 主线程
}

// 这是暴露给 Node.js 使用的函数
napi_value StartLongComputation(napi_env env, napi_callback_info info) {
    // 创建一个线程并开始长时间计算任务
    pthread_t thread_id;
    pthread_create(&thread_id, NULL, LongComputationTask, NULL);

    // 通知 Node.js 不要退出,即使事件循环为空
    napi_ref_threadsafe_function(env, ...);

    ...
}

在上面的代码中,我们没有详细展示如何使用 napi_ref_threadsafe_function 来引用线程安全函数,因为这涉及到更复杂的 N-API 用法,包括创建 napi_threadsafe_function 和正确管理引用计数。

例子 2:停止后台任务

当后台任务完成时,你需要告诉 Node.js 它可以退出了,如果没有其他任务在运行的话。这是通过 napi_unref_threadsafe_function 来实现的:

// 当后台任务完成后被调用
void OnTaskComplete(napi_env env, void* data) {
    // 告诉 Node.js 它可以退出了
    napi_unref_threadsafe_function(env, ...);
}

在这种情况下,一旦 OnTaskComplete 被调用,就会减少 napi_threadsafe_function 的引用计数。如果没有其他活动阻止 Node.js 退出,那么进程可能会在此之后结束。

最关键的是理解,通过这些 API,你可以精确地控制 Node.js 进程的生命周期,从而确保不会过早地退出,也不会无必要地挂起。在实际应用中,使用这些功能通常涉及到异步编程和对 Node.js 事件循环的深入理解。

napi_create_threadsafe_functionopen in new window

Node.js 中的 napi_create_threadsafe_function 是一个非常重要的功能,它允许你在原生扩展中安全地调用 JavaScript 函数,即使是在多线程的环境下。理解这个功能之前,先需要了解几个基本概念。

基本概念

  1. N-API: Node.js 的一个 API 层,允许你使用 C 或者 C++ 编写扩展模块,这些模块可以直接与 Node.js 的 V8 引擎交互。
  2. Thread-safe: 当多个线程同时运行代码时,如果每次运行结果和单线程运行结果一致,并且不会引发运行时错误,则认为这段代码是“线程安全”的。

当你编写一个能够执行异步操作(例如文件读写、网络请求等)的 Node.js 原生扩展时,你可能需要在后台线程中执行一些任务,而完成这些任务后,你又想回到主线程中来更新 JavaScript 环境或调用 JavaScript 函数。在这种情况下,你不能简单地从后台线程直接调用 JavaScript 函数,因为 V8 不是线程安全的。所以你需要使用 napi_create_threadsafe_function 来创建一个可以跨线程安全调用的函数。

工作机制

napi_create_threadsafe_function 允许你将一个 JavaScript 函数封装成一个可以从任何线程安全调用的 ThreadsafeFunction。内部机制大致如下:

  • 在主线程中,你调用 napi_create_threadsafe_function 创建一个 ThreadsafeFunction
  • 在后台线程中,你通过调用 napi_call_threadsafe_function 来触发这个 JavaScript 函数的执行。
  • Node.js 会确保不管在哪个线程调用该函数,最终都会在一个安全的上下文(比如主线程或事件循环的某个阶段)中执行原始的 JavaScript 函数。

实际例子

假设你正在写一个原生模块,需要从一个 C/C++ 的后台线程获取数据然后在 JavaScript 端进行处理。

  1. 首先,在 JavaScript 层面定义一个回调函数
function processData(data) {
  // 这里的 data 将是从 C/C++ 后台线程传递过来的数据
  console.log("处理数据:", data);
}
  1. 在原生模块中,创建一个 ThreadsafeFunction
##include `<`node_api.h>

// 假设我们有一个 processData 函数指针已经传入
napi_value processDataFn;
napi_value resourceName;
napi_threadsafe_function tsfn;

napi_create_string_utf8(env, "ResourceName", NAPI_AUTO_LENGTH, &resourceName);
napi_create_threadsafe_function(env, processDataFn, NULL, resourceName, 0, 1,
                                NULL, NULL, NULL, CallJs, &tsfn);
  1. 在后台线程中,你可以调用 napi_call_threadsafe_function 触发这个函数
void* run_thread(void* arg) {
  // 模拟产生数据
  int data = 100;
  napi_call_threadsafe_function(tsfn, &data, napi_tsfn_blocking);
  return NULL;
}

// somewhere in your code to start a new thread
pthread_t thread;
pthread_create(&thread, NULL, run_thread, NULL);
  1. 最后,清理资源

当你完成所有工作并且不再需要 ThreadsafeFunction,你应该释放它:

napi_release_threadsafe_function(tsfn, napi_tsfn_release);

以上,是对 napi_create_threadsafe_function 的一个简化的解释和例子。在实际的开发中,你将需要管理更复杂的情况,比如错误处理、正确的资源清理等。但基本原则是:使用 napi_create_threadsafe_function 可以在多线程环境中安全地调用 JavaScript 函数。

napi_get_threadsafe_function_contextopen in new window

好的,让我们来聊一聊 Node.js 中的 napi_get_threadsafe_function_context 函数。

首先,要了解这个 API,你需要知道 N-API 是 Node.js 提供的一个用 C 编写的插件 API,它独立于底层的 JavaScript 运行时(比如 V8)并且是稳定的。也就是说,使用 N-API 编写的原生模块不用担心 Node.js 版本升级导致的兼容性问题。

再来,Thread-safe function(线程安全函数)是 N-API 中一个特殊概念。在多线程环境中,当你尝试从一个非主线程访问 Node.js 的环境时,通常会出现问题,因为 Node.js 的大部分核心都不是线程安全的。为了在背景工作线程与主事件循环之间安全地传递数据,N-API 提供了线程安全函数。

现在,当我们谈论napi_get_threadsafe_function_context,我们指的是从已经创建的线程安全函数中检索其上下文。每个线程安全函数都有一个关联的上下文,你在创建它的时候可以指定。这个上下文是个“黑盒子”,可以是任何你想要的数据结构,你可能会在调用该函数时用到这个上下文数据。

下面我会给你一个例子如何实际应用napi_get_threadsafe_function_context

假设你正在编写一个原生 Node.js 模块,这个模块会在后台线程中执行一些耗时操作,然后需要将结果回传给 Node.js 的主线程。在这个场景中,你可能会创建一个线程安全函数,并且带有一些上下文数据,例如操作的类型或者其他状态信息。

// 假设这是你的上下文结构体
typedef struct {
  int operationType;
  // ... 可能还有其他状态信息
} MyContext;

// 创建线程安全函数时的某个回调函数
void CallJs(napi_env env, napi_value js_callback, void* context, void* data) {
  // 将context转化为你自己的上下文结构体
  MyContext* my_context = (MyContext*)context;

  // 你可以根据my_context中的信息来决定怎么处理data
  // 然后你可能会调用js_callback来将结果传递给Node.js的主线程
}

// ... 在某个地方你创建了线程安全函数
napi_threadsafe_function tsfn;
napi_create_threadsafe_function(
  env,
  js_callback, // JavaScript回调函数,将在主线程中被调用
  async_resource,
  async_resource_name,
  0, // 无限队列大小
  1, // 初始线程数
  my_context, // 这里你传入了上下文信息
  finalize_cb, // 当清除线程安全函数时调用的回调
  NULL, // finalize_hint
  CallJs, // 上面定义的CallJs功能
  &tsfn
);

// ... 现在在你的后台线程中,你可能需要获取上下文做一些处理
void BackgroundThreadWork(napi_threadsafe_function tsfn) {
  // 从tsfn获得上下文
  void* context;
  napi_status status = napi_get_threadsafe_function_context(tsfn, &context);
  if (status == napi_ok) {
    MyContext* my_context = (MyContext*)context;

    // 现在你拥有了上下文,可以根据上下文中保存的信息进行相应的工作
  }

  // ...执行你的背景工作

  // 完成后,你可能想要调用线程安全函数来通知Node.js的主线程
}

以上代码展示了如何创建一个带有上下文的线程安全函数,并在后台线程中使用napi_get_threadsafe_function_context来获取这个上下文。这样,你就可以在多线程环境中安全地与 Node.js 的主线程进行通信了。

napi_call_threadsafe_functionopen in new window

Node.js 中的napi_call_threadsafe_function是一个函数,用于在 N-API(Node.js 的原生 API)中安全地从任何线程调用 JavaScript 函数。这个功能主要用于当你有一个原生插件(通常用 C 或者 C++写的模块)需要执行异步操作或者从不同的线程调用 Node.js 代码时。

首先,让我们理解什么是线程安全。当多个线程尝试同时访问相同的资源时,如果没有适当的同步机制,就可能会发生冲突和不可预知的行为。因此,线程安全是指代码可以被多个线程安全地调用,而不会产生竞争条件或其他问题。

在 Node.js 中,JavaScript 运行在单一的线程上,称为事件循环线程。但是,当你使用原生模块和异步 I/O 操作时,可能会涉及额外的线程。这时候如果你想要从这些附加线程安全地调用 JavaScript 函数,就需要用到napi_call_threadsafe_function

下面是一个简化的例子,展示了如何使用napi_call_threadsafe_function

假设你正在编写一个 Node.js 扩展,该扩展通过原生代码连接到某个服务,并且需要在接收到数据时回调 JavaScript 函数。

##include `<`node_api.h>

// 假设这是从服务收到数据时要调用的JavaScript回调函数
napi_value js_callback;

// 这个结构体用来存储线程安全函数相关信息
napi_threadsafe_function tsfn;

// 从其他线程调用这个函数以触发JavaScript回调
void service_callback(void* data) {
    // `data`是你从服务接收到的数据

    // 调用JavaScript回调函数,确保它是线程安全的
    napi_status status = napi_call_threadsafe_function(tsfn, data, napi_tsfn_blocking);
    if (status != napi_ok) {
        // 处理错误...
    }
}

// 这是创建线程安全函数并启动服务监听器的初始化代码
napi_value Init(napi_env env, napi_value exports) {
    // ...初始化代码...

    // 获取JavaScript提供的回调函数,并保存引用
    napi_value js_callback;
    // 假设js_callback已经被正确获取并赋值...

    // 创建线程安全函数
    napi_create_threadsafe_function(env, js_callback, NULL, NAPI_AUTO_LENGTH, 0, 1, NULL, NULL, NULL, service_callback, &tsfn);

    // 启动服务监听器...
    // 当服务有数据时,service_callback将被调用

    return exports;
}

// 注册模块
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的代码中,napi_threadsafe_function类型的变量tsfn被用来存储线程安全函数的信息。通过调用napi_create_threadsafe_function创建一个线程安全的函数,然后可以从任何线程通过napi_call_threadsafe_function调用该 JavaScript 函数。

注意:以上代码只是概念性的示例,实际使用时还需要考虑错误处理、资源释放和线程同步等问题。

重要的点是,当你从非事件循环线程调用 JavaScript 代码时,必须采取线程安全的措施,否则可能会破坏 Node.js 的单线程模型,导致不可预期的结果。napi_call_threadsafe_function正是提供了一种安全的方式来完成这样的任务。

napi_acquire_threadsafe_functionopen in new window

Node.js 的 N-API(原生 API)提供了一套在 Node.js 中使用 C 或 C++编写扩展的接口。这些接口允许本地代码安全地与 Node.js 的 JavaScript 运行时和对象进行交互。

napi_acquire_threadsafe_function 是 N-API 中的一个函数,它用于多线程环境中安全地调用 JavaScript 函数。因为 Node.js 是单线程运行 JavaScript 的,在多线程 C/C++扩展中直接调用 JavaScript 代码通常是不安全的。所以这个 API 函数可以帮助我们从其他线程安全地调用 JavaScript 代码。

下面我将简要解释 napi_acquire_threadsafe_function 的用法,并给出一些实际例子来说明它的运用场景。

解释

napi_acquire_threadsafe_function 用于“获取”一个线程安全函数,使得其他线程可以安全地调用这个函数。获取操作通常会增加内部引用计数,这样就可以确保在所有线程完成对该函数的使用前,函数不会被垃圾回收。

当你想在原生线程中调用一个 JavaScript 函数时,你首先需要通过 napi_create_threadsafe_function 创建一个线程安全函数,然后在每个线程开始调用之前使用 napi_acquire_threadsafe_function,并在调用结束后使用 napi_release_threadsafe_function 来释放它。

使用步骤

  1. 创建线程安全函数:使用 napi_create_threadsafe_function 创建一个可从任何线程调用的 JavaScript 函数。
  2. 获取线程安全函数:在准备调用该函数的线程中,使用 napi_acquire_threadsafe_function 来获取权利调用它。
  3. 调用函数:通过 napi_call_threadsafe_function 在任何线程里面调用这个函数。
  4. 释放线程安全函数:调用完成后,使用 napi_release_threadsafe_function 释放你之前获得的权利。

实际运用的例子

假设您正在编写一个 Node.js 扩展,该扩展需要执行一些耗时的计算,而这个计算过程是在原生的 C/C++线程中完成的。你希望在计算完成后,能够安全地更新 Node.js 的某个 JavaScript 对象或调用某个 JavaScript 函数。

例如:

##include `<`node_api.h>

// 假设这是您想在JavaScript中调用的函数
void JSFunctionCall(napi_env env, napi_value js_callback, void* context) {
    // ... 这里可能是调用 JavaScript 函数的代码 ...
}

// 原生线程的工作函数
void* NativeThreadWork(void* arg) {
    napi_threadsafe_function ts_fn = (napi_threadsafe_function)arg;

    // 获取权限
    napi_acquire_threadsafe_function(ts_fn);

    // 执行一些工作...

    // 调用JavaScript函数
    napi_call_threadsafe_function(ts_fn, nullptr, napi_tsfn_blocking);

    // 释放权限
    napi_release_threadsafe_function(ts_fn, napi_tsfn_release);

    return nullptr;
}

// ...

在上述伪代码示例中,我们首先在主线程中创建了一个线程安全函数 ts_fn,然后传递给了原生线程 NativeThreadWork。在原生线程中,我们通过 napi_acquire_threadsafe_function 获取了调用 JavaScript 函数的权限,并在完成调用后通过 napi_release_threadsafe_function 释放了该权限。这样就可以确保在多线程环境下,我们的 JavaScript 函数调用是安全的。

总结来说,napi_acquire_threadsafe_function 是一个关键的函数,它在 Node.js 的 N-API 中使得多线程原生扩展能够安全地与 JavaScript 代码交互。通过使用它,开发者可以确保即使在多线程的情况下,也能安全地调用 JavaScript 函数,而不会导致竞态条件或崩溃等问题。

napi_release_threadsafe_functionopen in new window

在 Node.js 中,N-API 是一种用于构建原生插件的 API。它允许你使用 C 或 C++等语言编写可以被 Node.js 直接调用的代码块。这些原生插件通常用于执行性能敏感的任务,例如图像处理、数据库访问或其他需要高性能计算的操作。

napi_release_threadsafe_function 是 N-API 中的一个函数,用于管理所谓的“线程安全函数”(thread-safe function)。这个概念可能对于初学者来说有些复杂,所以我会尽量用简单的例子来解释。

在 JavaScript 中,通常我们只在一个主线程上运行代码,而 Node.js 也是基于这样的事件驱动和非阻塞 I/O 模型。然而,在某些情况下,你可能需要在后台线程上执行一些任务,以避免阻塞主线程。比如当你在处理大量数据计算时,你不希望它影响到其他服务器请求的处理。

在这些后台线程上完成工作后,你可能需要将一些结果传回给主线程。这就是线程安全函数的用途:它允许从任何线程安全地调用 JavaScript 函数。

现在,假设你建立了一个线程安全函数,你的后台线程正在使用它来定期将数据发送回主线程。当你的应用程序结束或者你不再需要这个线程安全函数时,你必须告诉 Node.js,它可以清理与该函数相关的资源。这正是napi_release_threadsafe_function的作用。调用这个函数会减少线程安全函数的引用计数,当引用计数降到零时,Node.js 就会释放它占用的资源。

实际应用的例子:

  1. 异步日志记录:你可能想要在后台线程上进行日志记录,以免影响主线程的性能。每当后台线程需要记录信息时,它可以通过线程安全函数来完成。完成日志记录任务后,使用napi_release_threadsafe_function来释放资源。

  2. 数据处理:如果你的应用程序接收到大量的数据需要处理,比如处理图像或分析文件,你可以在后台线程中处理这些数据,然后通过线程安全函数发送处理结果回主线程。最后,用napi_release_threadsafe_function来告知 Node.js 该线程安全函数不再需要。

  3. 实时数据更新:考虑一个股票市场应用程序,你可能在后台线程中实时获取股价并更新数据库。同时,你可能还需要及时将更新推送给前端显示。线程安全函数可以用于将这些更新发送回主线程,以便进一步处理。当这个功能不再需要时,再次调用napi_release_threadsafe_function

在使用napi_release_threadsafe_function时,你需要考虑同步问题,确保没有其他线程试图在你释放资源后还使用该函数。正确地管理线程安全函数的创建和释放是开发稳定原生插件的重要方面。

napi_ref_threadsafe_functionopen in new window

Node.js 中的 N-API(原生 API)是一个用于构建原生插件(通常是用 C 或 C++编写的模块)的底层 API。它提供了一组稳定的函数,允许原生代码与 JavaScript 代码交互。

在这个上下文中,napi_ref_threadsafe_function 是一个非常特殊的函数,它涉及到多线程和异步编程。我将首先解释什么是 threadsafe function,然后举例说明它如何被使用。

什么是 Threadsafe Function?

在 Node.js 中,由于 JavaScript 运行在单线程环境中(主要是事件循环),所以在处理长时间运行的任务时通常会使用异步操作来避免阻塞该线程。但是,在某些情况下,你可能需要从一个不同的线程(可能是一个原生扩展创建的线程)中执行代码,并最终要将结果传递回 JavaScript 的主线程。为此,Node.js 提供了 ThreadsafeFunction 类型的对象,即可以安全地从任何线程调用的函数。

napi_ref_threadsafe_function 功能

napi_ref_threadsafe_function 函数的作用是增加对已存在的 ThreadsafeFunction 对象的引用计数。当你创建一个 threadsafe function 时,它的引用计数默认为 1。每次调用 napi_ref_threadsafe_function,引用计数会增加。这样做是为了防止该函数对象被垃圾回收机制回收,只有当引用计数降到 0 时,垃圾回收器才会考虑回收该对象。

为什么需要引用 threadsafe function?

当你的程序创建了一个 threadsafe function 并且在多个地方使用时,你需要确保当它仍被需要时不会被回收。通过增加引用计数,你可以控制对象生命周期,并确保只有在所有地方都完成使用后,对象才能被清除。

实际运用的例子

想象你正在编写一个 Node.js 应用程序,这个应用程序需要执行 CPU 密集型的图像处理操作。为了不阻塞事件循环,你决定使用 C++编写一个原生模块来在背景线程进行图像处理。当处理完成时,你需要将处理结果返回到 JavaScript 主线程中更新 UI 或进一步处理。

以下是一个简化的示例流程:

  1. 在 JavaScript 代码中,你引入了自己的原生模块并创建了一个 threadsafe function。
  2. 你把这个 threadsafe function 传递给原生模块。
  3. 原生模块开始在另一个线程上处理图像。
  4. 处理完毕后,原生模块使用 threadsafe function 将结果传回 JavaScript 主线程。
  5. 为确保 threadsafe function 在完成工作前不被回收,原生模块在开始工作前调用了 napi_ref_threadsafe_function 来增加引用计数。
  6. 当结果返回给 JavaScript 测并且不再需要 threadsafe function 时,原生模块调用 napi_unref_threadsafe_function 来减少引用计数。

通过这种方式,你就能够安全地在多个线程间传递信息而不会破坏 Node.js 的单线程模型,并且不必担心对象在使用过程中被意外回收。

napi_unref_threadsafe_functionopen in new window

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时,它允许你在服务器端运行 JavaScript 代码。N-API 是 Node.js 提供的一组 C 语言 API,它使得创建原生插件变得简单,而且保证了与 Node.js 不同版本的兼容性。

napi_unref_threadsafe_function 是 N-API 中的一个函数,它用于管理一个叫做「线程安全函数」(thread-safe function)的引用计数。在解释这个函数之前,先说明几个背景知识点:

  1. 线程安全函数: 在 Node.js 中,JavaScript 通常在单一的主线程上运行,这样避免了多线程编程中的复杂性。但是,当需要执行耗时的操作时(例如文件读写、网络请求),可以使用其他线程来避免阻塞主线程。线程安全函数就是为了在这些工作线程和主线程之间安全地传递调用而设计的。

  2. 引用计数: 引用计数是一种内存管理技术,用于跟踪一个对象被引用的次数。当一个对象的引用计数降到零时,意味着没有任何地方正在使用这个对象,因此它可以被垃圾回收。

现在让我们来看看 napi_unref_threadsafe_function 本身:

  • 作用napi_unref_threadsafe_function 函数用于减少线程安全函数的引用计数。当一个线程不再需要调用该线程安全函数时,应该调用这个函数。

  • 为什么要减少引用计数:如果你创建了一个线程安全函数,但是永远不去减少它的引用计数,即使你的代码不再需要它,它也不会被垃圾回收。这可能会导致内存泄漏。

  • 如何使用:只有当你之前通过 napi_ref_threadsafe_function 增加了引用计数时,才需要调用 napi_unref_threadsafe_function 来减少引用计数。

实际运用的例子

假设你有一个 Node.js 插件,该插件启动了一个新的线程来处理数据,处理完后需要将结果传回给 JavaScript 的主线程。你可以创建一个线程安全函数来做到这一点。

##include `<`node_api.h>

// 线程安全函数的句柄
napi_threadsafe_function tsfn;

// 这是在工作线程中调用的函数
void worker_thread_function(void* data) {
    // ... 执行一些操作 ...

    // 通知 Node.js 主线程执行回调
    napi_call_threadsafe_function(tsfn, data, napi_tsfn_blocking);
}

void some_init_function(napi_env env) {
    // 创建一个线程安全函数
    napi_create_threadsafe_function(
        env,
        ...,
        &tsfn
    );

    // 增加引用计数,因为我们打算在工作线程中使用它
    napi_ref_threadsafe_function(env, tsfn);

    // ... 启动工作线程 ...
}

void some_cleanup_function(napi_env env) {
    // 减少引用计数,因为我们不再需要在工作线程中使用它
    napi_unref_threadsafe_function(env, tsfn);

    // 释放线程安全函数
    napi_release_threadsafe_function(tsfn, napi_tsfn_release);
}

上面的代码片段展示了如何在一个原生模块中创建并使用线程安全函数。注意,在 some_cleanup_function 函数中,我们调用了 napi_unref_threadsafe_function 来减少由 napi_ref_threadsafe_function 增加的引用计数。这样确保了当这个线程安全函数不再需要时,它能够被正确地清理和垃圾回收。

Miscellaneous utilitiesopen in new window

在 Node.js 中,Miscellaneous utilities通常指的是一系列的辅助函数或工具,它们可以帮助开发者完成不同的任务。在 N-API(即 Node-API)的上下文中,Miscellaneous utilities指的是那些和直接 API 功能不太相关,但仍然很有用的一些函数。

由于你提到的是 Node.js v21.7.1 中的内容,我们将聚焦于 N-API 中的Miscellaneous utilities。N-API 是一个 C 语言写的 API,它对原生插件(native addons)作者来说是稳定的,并且兼容不同版本的 Node.js。原生插件是使用 C 或 C++编写的模块,可以直接调用底层系统 API,这在性能上有一定的优势,因为它们运行在更低级别,更靠近硬件。

Miscellaneous utilities部分,我们可能会看到以下几种类型的工具:

  1. 类型检查工具 - 这包括了函数,比如napi_is_array(),它可以让你检查 JavaScript 值是否是一个数组。

  2. 错误处理工具 - 如napi_throw_error(),它用于生成并抛出一个 JavaScript 错误。

  3. 引用管理工具 - 比如napi_refnapi_unref,它们用于管理对 JavaScript 对象的引用,在原生代码中保持对象活动状态,防止被垃圾回收器回收。

  4. 线程安全功能 - 比如napi_acquire_threadsafe_function()napi_release_threadsafe_function(),它们用于在多线程环境下安全地调用 JavaScript 函数。

实际应用的例子:

类型检查: 假设你正在编写一个原生模块,需要验证传入的参数是否为数组:

napi_value my_function(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value argv[1];
    napi_get_cb_info(env, info, &argc, argv, NULL, NULL);

    napi_valuetype valuetype;
    napi_typeof(env, argv[0], &valuetype);

    if (valuetype != napi_object) {
        napi_throw_type_error(env, NULL, "Argument is not an object");
    }

    bool is_array;
    napi_is_array(env, argv[0], &is_array);

    if (!is_array) {
        napi_throw_type_error(env, NULL, "Object is not an array");
    }

    // 继续处理数组...
}

错误处理: 如果你在原生模块的某个操作中遇到了错误,你可能想要抛出一个错误给 JavaScript:

napi_value my_function_that_may_fail(napi_env env, napi_callback_info info) {
    // 假设do_something_risky是一个可能失败的操作
    bool success = do_something_risky();

    if (!success) {
        napi_throw_error(env, NULL, "Something risky failed!");
        return NULL;
    }

    // 如果成功,则继续执行...
}

引用管理: 当你需要在原生模块中存储一个 JavaScript 对象,并确保它不会在原生代码还在使用它时被垃圾收集器回收时,你可以使用引用管理:

napi_ref my_ref;

void store_js_object(napi_env env, napi_value obj) {
    napi_create_reference(env, obj, 1, &my_ref);
}

void use_stored_js_object(napi_env env) {
    napi_value obj;
    napi_get_reference_value(env, my_ref, &obj);

    // 使用这个对象...
}

void cleanup(napi_env env) {
    napi_delete_reference(env, my_ref);
}

以上就是 Node.js 中 N-API Miscellaneous utilities的概览和一些简单的应用示例。这些工具为原生模块的开发提供了必要的辅助功能,使得与 JavaScript 交互更加安全和有效率。

node_api_get_module_file_nameopen in new window

Node.js 中的 node_api_get_module_file_name 是 N-API 的一部分,N-API 是一个用来构建原生插件的 API。换句话说,如果你想要在 Node.js 中使用 C 或 C++ 写一些代码来提升性能或者访问操作系统底层功能,那么这个 API 就是为此而设计的。

什么是 node_api_get_module_file_name?

node_api_get_module_file_name 是一个函数,它的作用是帮助你获取当前正在执行的原生模块的文件名。原生模块是用 C 或 C++ 编写的,并且被编译成 .node 文件,通过 Node.js 加载和运行的。

如何使用它?

这个函数需要在 C 或 C++ 的代码中使用,通常在一个称为“初始化函数”的地方。初始化函数是当 Node.js 加载原生模块时会调用的。在初始化函数内,你可以调用 node_api_get_module_file_name 来获取包含该模块的完整路径。

##include `<`node_api.h>

napi_value Init(napi_env env, napi_value exports) {
  // 获取模块文件名
  const char* module_file_name;
  napi_status status = node_api_get_module_file_name(env, &module_file_name);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 现在 module_file_name 包含了模块的完整文件路径

  return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

在上面的例子中,Init 函数就是我们提到的初始化函数。当 Node.js 加载原生模块时,它会调用这个函数。在这个函数里,我们使用 node_api_get_module_file_name 来获取模块的文件名。

实际应用例子

让我们来看一个实际的场景,假设你正在编写一个 Node.js 原生模块,该模块需要读取自身的源码或者相关数据文件。

例如,你可以有如下的 C++ 代码:

#include `<`fstream>
#include `<`string>
##include `<`node_api.h>

napi_value ReadOwnSource(napi_env env, napi_callback_info args) {
  const char* module_file_name;
  napi_status status = node_api_get_module_file_name(env, &module_file_name);
  if (status != napi_ok) {
    // 处理错误...
  }

  // 打开模块文件进行读取
  std::ifstream file(module_file_name);
  std::string file_contents((std::istreambuf_iterator`<`char>(file)),
                             std::istreambuf_iterator`<`char>());

  // 转换为 JavaScript 字符串返回给 JS
  napi_value result;
  status = napi_create_string_utf8(
    env,
    file_contents.c_str(),
    file_contents.length(),
    &result
  );
  if (status != napi_ok) {
    // 处理错误...
  }

  return result;
}

// 还需要注册函数等操作...

以上的 C++ 代码定义了一个函数 ReadOwnSource,这个函数使用 node_api_get_module_file_name 来获取当前模块的文件名,然后打开这个文件并读取内容,最后把内容作为字符串返回给 Node.js 端。

这只是一个简化的例子,实际使用中,你可能还需要处理各种错误情况以及安全问题,但它展示了如何使用 node_api_get_module_file_name 函数以及它的实际用途。