使用Node-API进行class相关开发
=====================

#### 简介

使用Node-API接口进行class相关开发，处理ArkTS中的类，例如定义类、构造实例等。

#### 基本概念

在使用Node-API接口进行class相关开发时，需要理解以下基本概念：

*   **类**：类是用于创建对象的模板。它提供了一种封装数据和行为的方式，以便于对数据进行处理和操作。类在ArkTS中是建立在原型（prototype）的基础上的，并且还引入了一些类独有的语法和语义。
*   **实例**：实例是通过类创建具体的对象。类定义了对象的结构和行为，而实例则是类的具体表现。通过实例化类，我们可以访问类中定义的属性和方法，并且每个实例都具有自己的属性值。
*   **原型**：ArkTS也采用Class的概念来实现类型之间的继承，早期EcmaScript规范定义了原型的概念，对象通过原型链的方式来实现继承的。原型的概念可以参考[EcmaScript的社区规范](https://262.ecma-international.org/#sec-terms-and-definitions-prototype)。

#### 场景和功能介绍

以下Node-API接口主要用于处理class。他们的使用场景如下：

| 接口 | 描述 |
| :-- | :-- |
| napi\_new\_instance | 需要通过给定的构造函数构建一个实例时，可以使用这个函数。 |
| napi\_get\_new\_target | 使用此函数获取构造函数调用的new.target。 |
| napi\_define\_class | 在Node-API模块定义与ArkTS类相对应的类。这个函数允许将Node-API模块类绑定到ArkTS类。 |
| napi\_wrap | 在ArkTS对象上绑定一个Node-API模块对象实例。这个函数通常在将Node-API模块对象与ArkTS对象进行绑定时使用，以便在ArkTS中使用本地对象的方法和属性。 |
| napi\_unwrap | 从ArkTS对象上获取之前绑定的Node-API模块对象实例。 |
| napi\_remove\_wrap | 从ArkTS对象上获取之前绑定的Node-API模块对象实例，并解除绑定。 |

#### 使用示例

Node-API接口开发流程参考[使用Node-API实现跨语言交互开发流程](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-process)，本文仅对接口对应C++及ArkTS相关代码进行展示。

#### \[h2\]napi\_new\_instance

通过给定的构造函数实例化一个对象，将这个对象返回ArkTS侧使用。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/51/v3/eQMysOZ5SWuARdzaxNClwQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073808Z&HW-CC-Expire=86400&HW-CC-Sign=778AF001CB5B2217748164E765A951717B4E018EA7317393288E35C39233C743)

参数constructor不是function类型则返回napi\_function\_expected。

cpp部分代码

// napi\_new\_instance
static napi\_value NewInstance(napi\_env env, napi\_callback\_info info)
{
    // 传入并解析参数，第一个参数为传入的构造函数，第二个参数为需要传入构造函数的参数
    size\_t argc = 2;
    napi\_value args\[2\] = {nullptr};
    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    // 调用napi\_new\_instance接口，实例化一个对象，将这个对象返回
    napi\_value result = nullptr;
    napi\_new\_instance(env, args\[0\], 1, &args\[1\], &result);
    return result;
}

接口声明

export const newInstance: (obj: Object, param: string) => Object; // napi\_new\_instance

ArkTS侧示例代码

class Fruit {
  name: string;

  constructor(name: string) {
    this.name = name;
  }
}

// napi\_new\_instance
// 调用函数，用变量obj接收函数返回的实例化对象
let obj = testNapi.newInstance(Fruit, 'test');
// 打印实例化对象obj的信息
hilog.info(0x0000, 'Node-API', 'napi\_new\_instance %{public}s', JSON.stringify(obj));

#### \[h2\]napi\_get\_new\_target

用于获取构造函数的new.target值。在ArkTS中，new.target是一个特殊的元属性，用于在构造函数中判断是否通过new关键字调用了该构造函数。

示例代码可以参考链接：

[Native与ArkTS对象绑定](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-object-wrap)

#### \[h2\]napi\_define\_class

用于定义一个ArkTS类。该函数允许在Node-API模块中创建一个ArkTS类，并将类的方法和属性与相应的Node-API模块关联起来。

示例代码可以参考链接：

[Native与ArkTS对象绑定](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/use-napi-object-wrap)

#### \[h2\]napi\_wrap

在ArkTS object上绑定一个native对象实例。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/03/v3/OLbecTrGRhuZQ5r9x-qHfQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073808Z&HW-CC-Expire=86400&HW-CC-Sign=20143CAF6061F76D7A542EB053A621C2D7EC9BCDA198393248E0187F4389DC4E)

参数js\_object不为object类型或function类型时返回napi\_object\_expected。

#### \[h2\]napi\_unwrap

从一个被包装的对象中获取与之关联的数据指针。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/9a/v3/5oFNPe_cQheGJS-UDh1Ujw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073808Z&HW-CC-Expire=86400&HW-CC-Sign=E5025CE9EE929C42FD33F38E53ACE992DF24AB171693629E4F8CEF08D2A1DA3E)

参数js\_object不为object类型或function类型时返回napi\_object\_expected。

#### \[h2\]napi\_remove\_wrap

从ArkTS object上获取先前绑定的native对象实例，并解除绑定。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a9/v3/rvRwZBPUTxaH95npKrG_lw/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073808Z&HW-CC-Expire=86400&HW-CC-Sign=0F6F380A9C46AB8A0D1E86DE59F0D04637B144CBF6BD845471237DF936FFF165)

参数js\_object不为object类型或function类型时返回napi\_object\_expected。

cpp部分代码

struct Object {
    std::string name;
    int32\_t age;
};

static void DerefItem(napi\_env env, void \*data, void \*hint)
{
    // 可选的原生回调，用于在ArkTS对象被垃圾回收时释放原生实例
    OH\_LOG\_INFO(LOG\_APP, "Node-API DerefItem");
    Object \*obj = reinterpret\_cast<Object \*>(data);
    if (obj != nullptr) {
        delete obj;
    }
}

// napi\_wrap
static napi\_value Wrap(napi\_env env, napi\_callback\_info info)
{
    OH\_LOG\_INFO(LOG\_APP, "Node-API wrap");
    // 初始化Node-API模块的object
    struct Object \*obj = new struct Object();
    obj->name = "liLei";
    obj->age = INT\_ARG\_18;
    size\_t argc = 1;
    napi\_value toWrap;
    // 调用napi\_wrap将Node-API模块的object绑定到ArkTS object上
    napi\_status status\_cb = napi\_get\_cb\_info(env, info, &argc, &toWrap, NULL, NULL);
    if (status\_cb != napi\_ok) {
        OH\_LOG\_ERROR(LOG\_APP, "napi\_get\_cb\_info failed");
        delete obj;
        return nullptr;
    }
    napi\_status status = napi\_wrap(env, toWrap, reinterpret\_cast<void \*>(obj), DerefItem, NULL, NULL);
    if (status != napi\_ok) {
        // 主动释放内存
        delete obj;
    }

    return toWrap;
}

// napi\_remove\_wrap
static napi\_value RemoveWrap(napi\_env env, napi\_callback\_info info)
{
    OH\_LOG\_INFO(LOG\_APP, "Node-API removeWrap");
    size\_t argc = 1;
    napi\_value wrapped = nullptr;
    void \*data = nullptr;
    // 调用napi\_remove\_wrap从一个被包装的对象中解除包装
    napi\_get\_cb\_info(env, info, &argc, &wrapped, nullptr, nullptr);
    napi\_status status = napi\_remove\_wrap(env, wrapped, &data);
    if (status != napi\_ok || data == nullptr) {
        OH\_LOG\_ERROR(LOG\_APP, "Node-API napi\_remove\_wrap failed or data is nullptr");
        return nullptr;
    }

    return nullptr;
}

// napi\_unwrap
static napi\_value UnWrap(napi\_env env, napi\_callback\_info info)
{
    OH\_LOG\_INFO(LOG\_APP, "Node-API unWrap");
    size\_t argc = 1;
    napi\_value wrapped = nullptr;
    napi\_get\_cb\_info(env, info, &argc, &wrapped, nullptr, nullptr);
    // 调用napi\_unwrap取出绑定在ArkTS object中的数据并打印
    struct Object \*data = nullptr;
    napi\_status status = napi\_unwrap(env, wrapped, reinterpret\_cast<void \*\*>(&data));
    if (status != napi\_ok || data == nullptr) {
        OH\_LOG\_ERROR(LOG\_APP, "Node-API napi\_unwrap failed or data is nullptr");
        return nullptr;
    }
    OH\_LOG\_INFO(LOG\_APP, "Node-API name: %{public}s", data->name.c\_str());
    OH\_LOG\_INFO(LOG\_APP, "Node-API age: %{public}d", data->age);
    return nullptr;
}

接口声明

export const wrap: (obj: Object) => Object; // napi\_wrap

export const unWrap: (obj: Object) => void; // napi\_unwrap

export const removeWrap: (obj: Object) => void; // napi\_remove\_wrap

ArkTS侧示例代码

try {
  class Obj {
  }

  let obj: Obj = {};
  testNapi.wrap(obj); // napi\_wrap
  testNapi.unWrap(obj); // napi\_unwrap
  testNapi.removeWrap(obj); // napi\_remove\_wrap
  // ...
} catch (error) {
  hilog.error(0x0000, 'testTag', 'Test Node-API error: %{public}s', error.message);
  // ...
}

以上代码如果要在native cpp中打印日志，需在CMakeLists.txt文件中添加以下配置信息（并添加头文件：#include "hilog/log.h"）：

// CMakeLists.txt
add\_definitions( "-DLOG\_DOMAIN=0xd0d0" )
add\_definitions( "-DLOG\_TAG=\\"testTag\\"" )
target\_link\_libraries(entry PUBLIC libace\_napi.z.so libhilog\_ndk.z.so)