1# 使用Node-API进行class相关开发
2
3## 简介
4
5使用Node-API接口进行class相关开发,处理ArkTS中的类,例如定义类、构造实例等。
6
7## 基本概念
8
9在使用Node-API接口进行class相关开发时,需要理解以下基本概念:
10
11- **类**:类是用于创建对象的模板。它提供了一种封装数据和行为的方式,以便于对数据进行处理和操作。类在ArkTS中是建立在原型(prototype)的基础上的,并且还引入了一些类独有的语法和语义。
12- **实例**:实例是通过类创建具体的对象。类定义了对象的结构和行为,而实例则是类的具体表现。通过实例化类,我们可以访问类中定义的属性和方法,并且每个实例都具有自己的属性值。
13
14## 场景和功能介绍
15
16以下Node-API接口主要用于处理class。他们的使用场景如下:
17| 接口 | 描述 |
18| -------- | -------- |
19| napi_new_instance | 需要通过给定的构造函数构建一个实例时,可以使用这个函数。 |
20| napi_get_new_target | 使用此函数获取构造函数调用的new.target。 |
21| napi_define_class | 在Node-API模块定义与ArkTS类相对应的类。这个函数允许将Node-API模块类绑定到ArkTS类。 |
22| napi_wrap | 在ArkTS对象上绑定一个Node-API模块对象实例。这个函数通常在将Node-API模块对象与ArkTS对象进行绑定时使用,以便在ArkTS中使用本地对象的方法和属性。 |
23| napi_unwrap | 从ArkTS对象上获取之前绑定的Node-API模块对象实例。 |
24| napi_remove_wrap | 从ArkTS对象上获取之前绑定的Node-API模块对象实例,并解除绑定。 |
25
26## 使用示例
27
28Node-API接口开发流程参考[使用Node-API实现跨语言交互开发流程](use-napi-process.md),本文仅对接口对应C++及ArkTS相关代码进行展示。
29
30### napi_new_instance
31
32通过给定的构造函数实例化一个对象,将这个对象返回ArkTS侧使用。
33
34> **说明:**
35>
36> 参数constructor不是function类型则返回napi_function_expected。
37
38cpp部分代码
39
40```cpp
41static napi_value NewInstance(napi_env env, napi_callback_info info)
42{
43    // 传入并解析参数,第一个参数为传入的构造函数,第二个参数为需要传入构造函数的参数
44    size_t argc = 2;
45    napi_value args[2] = {nullptr};
46    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
47    // 调用napi_new_instance接口,实例化一个对象,将这个对象返回
48    napi_value result = nullptr;
49    napi_new_instance(env, args[0], 1, &args[1], &result);
50    return result;
51}
52```
53
54接口声明
55
56```ts
57// index.d.ts
58export const newInstance: (obj: Object, param: string) => Object
59```
60
61ArkTS侧示例代码
62
63```ts
64import hilog from '@ohos.hilog'
65import testNapi from 'libentry.so'
66class Fruit {
67  name: string;
68  constructor(name: string) {
69    this.name = name;
70  }
71}
72// 调用函数,用变量obj接收函数返回的实例化对象
73let obj = testNapi.newInstance(Fruit, 'test');
74// 打印实例化对象obj的信息
75hilog.info(0x0000, 'Node-API', 'napi_new_instance %{public}s', JSON.stringify(obj));
76```
77
78### napi_get_new_target
79
80用于获取构造函数的new.target值。在ArkTS中,new.target是一个特殊的元属性,用于在构造函数中判断是否通过new关键字调用了该构造函数。
81
82示例代码可以参考链接:
83
84[Native与ArkTS对象绑定](use-napi-object-wrap.md)
85
86### napi_define_class
87
88用于定义一个ArkTS类。该函数允许在Node-API模块中创建一个ArkTS类,并将类的方法和属性与相应的Node-API模块关联起来。
89
90示例代码可以参考链接:
91
92[Native与ArkTS对象绑定](use-napi-object-wrap.md)
93
94### napi_wrap
95
96在ArkTS object上绑定一个native对象实例。
97
98> **说明:**
99>
100> 参数js_object不为object类型或function类型时返回napi_object_expected。
101
102### napi_unwrap
103
104从一个被包装的对象中解除包装并获取与之关联的数据指针。
105
106> **说明:**
107>
108> 参数js_object不为object类型或function类型时返回napi_object_expected。
109
110### napi_remove_wrap
111
112从ArkTS object上获取先前绑定的native对象实例,并解除绑定。
113
114> **说明:**
115>
116> 参数js_object不为object类型或function类型时返回napi_object_expected。
117
118cpp部分代码
119
120```cpp
121#include <hilog/log.h>
122#include <string>
123#include "napi/native_api.h"
124
125struct Object {
126    std::string name;
127    int32_t age;
128};
129
130static void DerefItem(napi_env env, void *data, void *hint) {
131    // 可选的原生回调,用于在ArkTS对象被垃圾回收时释放原生实例
132    OH_LOG_INFO(LOG_APP, "Node-API DerefItem");
133    (void)hint;
134}
135
136static napi_value Wrap(napi_env env, napi_callback_info info)
137{
138    OH_LOG_INFO(LOG_APP, "Node-API wrap");
139    // 初始化Node-API模块的object
140    struct Object *obj = new struct Object();
141    obj->name = "liLei";
142    obj->age = 18;
143    size_t argc = 1;
144    napi_value toWrap;
145    // 调用napi_wrap将Node-API模块的object绑定到ArkTS object上
146    napi_get_cb_info(env, info, &argc, &toWrap, NULL, NULL);
147    napi_status status = napi_wrap(env, toWrap, reinterpret_cast<void *>(obj), DerefItem, NULL, NULL);
148    if (status != napi_ok) {
149        // 主动释放内存
150        delete obj;
151    }
152
153    return toWrap;
154}
155
156static napi_value RemoveWrap(napi_env env, napi_callback_info info)
157{
158    OH_LOG_INFO(LOG_APP, "Node-API removeWrap");
159    size_t argc = 1;
160    napi_value wrapped = nullptr;
161    void *data = nullptr;
162    // 调用napi_remove_wrap从一个被包装的对象中解除包装
163    napi_get_cb_info(env, info, &argc, &wrapped, nullptr, nullptr);
164    napi_remove_wrap(env, wrapped, &data);
165
166    return nullptr;
167}
168
169static napi_value UnWrap(napi_env env, napi_callback_info info)
170{
171    OH_LOG_INFO(LOG_APP, "Node-API unWrap");
172    size_t argc = 1;
173    napi_value wrapped = nullptr;
174    napi_get_cb_info(env, info, &argc, &wrapped, nullptr, nullptr);
175    // 调用napi_unwrap取出绑定在ArkTS object中的数据并打印
176    struct Object *data;
177    napi_unwrap(env, wrapped, reinterpret_cast<void **>(&data));
178    OH_LOG_INFO(LOG_APP, "Node-API name: %{public}s", data->name.c_str());
179    OH_LOG_INFO(LOG_APP, "Node-API age: %{public}d", data->age);
180    return nullptr;
181}
182```
183
184接口声明
185
186```ts
187// index.d.ts
188export const wrap: (obj: Object) => Object;
189export const unWrap: (obj: Object) => void;
190export const removeWrap: (obj: Object) => void;
191```
192
193ArkTS侧示例代码
194
195```ts
196import hilog from '@ohos.hilog'
197import testNapi from 'libentry.so'
198try {
199    class Obj {}
200    let obj: Obj = {};
201    testNapi.wrap(obj)
202    testNapi.unWrap(obj)
203    testNapi.removeWrap(obj)
204} catch (error) {
205    hilog.error(0x0000, 'testTag', 'Test Node-API error: %{public}s', error.message);
206}
207```
208
209以上代码如果要在native cpp中打印日志,需在CMakeLists.txt文件中添加以下配置信息(并添加头文件:#include "hilog/log.h"):
210
211```text
212// CMakeLists.txt
213add_definitions( "-DLOG_DOMAIN=0xd0d0" )
214add_definitions( "-DLOG_TAG=\"testTag\"" )
215target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
216```
217