Skip to content

什么是 "[native code]"?——JavaScript中的原生代码

1090字约4分钟

2025-01-16

在使用 JavaScript 进行开发时,你可能会经常看到一些函数或方法的源代码输出以 [native code] 结尾。那么这个术语究竟是什么意思呢?本文将深入探讨 [native code] 的含义,并为读者提供有关如何编写原生 JavaScript 代码的指导。

什么是 “[native code]”?

在 JavaScript 中,一个函数可以用两种方式定义:一种是使用 JavaScript 代码直接定义,另一种则是通过绑定到 JavaScript 引擎的底层 C 或 C++ 代码实现的。如果函数是由底层代码实现的,那么在调试器中查看其源代码时,就会显示 [native code]

底层代码通常是由浏览器厂商编写的,因此具有高度优化的性能和功能。例如,Array.prototype.push()Array.prototype.pop() 等方法就是由底层代码实现的。而在 JavaScript 引擎中运行的其他代码,则是由 JavaScript 解释器解释执行。

值得注意的是,对于底层代码实现的函数,虽然无法查看其源代码,但它们仍然遵循了相同的规则和语法,即可通过 JavaScript 代码进行调用和使用。

如何编写原生 JavaScript 代码?

1. 原生模块的定义

Node.js 支持使用 C 或 C++ 编写原生模块,这些模块可以直接调用系统级别的 API,从而实现更高效的性能和更强的功能。编写原生模块的主要步骤包括编写 C/C++ 代码、使用 node-gyp 编译模块、在 Node.js 中加载模块。

2. 环境搭建与基础代码编写

首先,确保你的开发环境中已安装了 node-gyp,可以通过以下命令安装:

npm install -g node-gyp

接下来,创建一个新的 Node.js 项目,并初始化:

mkdir my-native-module
cd my-native-module
npm init -y

然后,创建一个包含基本 C++ 代码的文件 my_module.cc:

#include <node.h>

namespace demo {
  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;

  void Method(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    args.GetReturnValue().Set(String::NewFromUtf8(isolate, "Hello, world!").ToLocalChecked());
  }

  void Initialize(Local<Object> exports) {
    NODE_SET_METHOD(exports, "hello", Method);
  }


  NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
}  // namespace demo

3. 使用 node-gyp 编译模块

在项目根目录下创建一个 binding.gyp 文件,内容如下:

{
  "targets": [
    {
      "target_name": "my_module",
      "sources": ["my_module.cc"]
    }
  ]
}

然后运行以下命令来编译模块:

node-gyp configure

node-gyp build

4. 在 Node.js 中加载原生模块

现在可以在 Node.js 中使用这个原生模块了:

const myModule = require('./build/Release/my_module');

console.log(myModule.hello()); // 输出: Hello, world!

利用 N-API 接口编写原生模块

1. N-API 的定义

N-API 是 Node.js 提供的一组 API,用于创建和操作 Node.js 对象。使用 N-API 可以编写与 V8 引擎无关的原生模块,从而提高模块的兼容性。

2 编写 N-API 模块

首先,创建一个包含基本 N-API 代码的文件 my_napi_module.cc

#include <node_api.h>

napi_value Method(napi_env env, napi_callback_info args) {
  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_value fn;
  napi_create_function(env, nullptr, 0, Method, nullptr, &fn);
  napi_set_named_property(env, exports, "hello", fn);
  return exports;
}


NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

3. 使用 node-gyp 编译 N-API 模块

与上面的方法类似,创建一个 binding.gyp 文件:

{
  "targets": [
    {
      "target_name": "my_napi_module",
      "sources": ["my_napi_module.cc"]
    }
  ]
}

然后运行以下命令来编译模块:

node-gyp configure
node-gyp build

4. Node.js 中加载 N-API 模块

现在可以在 Node.js 中使用这个 N-API 模块了:

const myNapiModule = require('./build/Release/my_napi_module');
console.log(myNapiModule.hello()); // 输出: Hello, world!

使用 FFI 库编写原生模块

1 FFI 的定义

FFI(Foreign Function Interface)是一个允许程序调用其他编程语言函数的机制。Node.js 中可以使用 ffi-napi 库来调用 C 函数。

2 安装 ffi-napi 库

首先,安装 ffi-napiref-napi 库:

npm install ffi-napi ref-napi

编写 C 代码并生成共享库

创建一个包含基本 C 代码的文件 my_library.c

#include <stdio.h>

void print_hello() {
    printf("Hello, world!\n");
}

然后编译生成共享库:

gcc -shared -o my_library.so my_library.c

3. Node.js 中加载 FFI 模块

在 Node.js 中使用 ffi-napi 库调用 C 函数:

const ffi = require('ffi-napi');
const ref = require('ref-napi');

// 定义共享库和函数
const myLibrary = ffi.Library('my_library', {
  print_hello: ['void', []]
})

// 调用函数
myLibrary.print_hello(); // 输出: Hello, world!

文章引用