1# Working with Objects Using Node-API
2
3## Overview
4
5Node-API provides APIs for basic ArkTS object operations, including creating an object, obtaining the prototype of an object, freezing or sealing an object, and checking the object type. You can use these APIs to manage ArkTS objects.
6
7## Basic Concepts
8
9You may need to define and operate objects when using Node-API in development. For example, define an API with an object as an input parameter, perform operations on the object, and have a result object returned. In this process, you need to ensure that the API definition is clear and compatible with the properties and methods of the object.
10
11- API: defines the interaction protocol between components. An API includes input parameters, output result, and possible error handling. By calling APIs, components can interact and exchange data with each other without knowing the internal implementation details.
12- Object: a composite data type that allows values of different types to be stored as an independent entity in ArkTS. An object is a collection of properties and methods. A property is a value associated with an object, and a method is an operation that the object can perform.
13
14## Available APIs
15
16The following table lists the APIs for operating and managing ArkTS objects.
17| API| Description|
18| -------- | -------- |
19| napi_get_prototype | Obtains the prototype of an ArkTS object. You can use this API to obtain the prototype object in C/C++.|
20| napi_create_object | Creates a default ArkTS object.|
21| napi_object_freeze | Freezes an ArkTS object. Once an object is frozen, its properties are immutable.|
22| napi_object_seal | Seals an ArkTS object. Once an object is sealed, its properties cannot be added or deleted, but property values can be modified.|
23| napi_typeof | Obtains the type of an ArkTS value.|
24| napi_instanceof | Checks whether an ArkTS object is an instance of the specified constructor.|
25| napi_type_tag_object | Associates the value of a tag pointer with an ArkTS object.|
26| napi_check_object_type_tag | Checks whether a tag pointer is associated with a ArkTS object.|
27| napi_create_symbol | Creates an ArkTS **Symbol** object.|
28| napi_create_external | Creates an ArkTS external object, which can be used to pass custom data structs or objects in C/C++ to ArkTS so that it can be accessible from ArkTS.|
29| napi_get_value_external | Obtains the ArkTS data from the external object created by **napi_create_external**. This API can be used to pass data between ArkTS and C/C++.|
30
31With these APIs, you can easily create and manipulate ArkTS objects in C/C++.
32
33## Example
34
35If you are just starting out with Node-API, see [Node-API Development Process](use-napi-process.md). The following demonstrates only the C++ and ArkTS code related to object management.
36
37### napi_get_prototype
38
39Use **napi_get_prototype** to obtain the prototype of an ArkTS object.
40
41CPP code:
42
43```cpp
44#include "napi/native_api.h"
45
46static napi_value GetPrototype(napi_env env, napi_callback_info info)
47{
48    // Obtain and parse input parameters.
49    size_t argc = 1;
50    napi_value args[1] = {nullptr};
51    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
52    napi_value result = nullptr;
53    // Obtain the prototype object of this object and return the prototype object obtained to the variable result of the napi_value type.
54    napi_get_prototype(env, args[0], &result);
55    return result;
56}
57```
58
59API declaration:
60
61```ts
62// index.d.ts
63export const getPrototype: (object: Object) => Object;
64```
65
66ArkTS code:
67
68```ts
69import hilog from '@ohos.hilog'
70import testNapi from 'libentry.so'
71// Define a class.
72class Person {
73  // Property.
74  name: string;
75  age: number;
76  // Constructor.
77  constructor(name: string, age: number) {
78    this.name = name;
79    this.age = age;
80  }
81}
82// Create a class instance.
83const person = new Person('Alice', 30);
84// Pass in an instance object and obtain the prototype of the object.
85let applePrototype = testNapi.getPrototype(person);
86// Check whether the prototype obtained by testNapi.getPrototype() is an Apple prototype.
87// ArkTS does not have the prototype concept in DevEco Studio 4.1 and later. When you perform prototype-related operations, 'Prototype assignment is not supported (arkts-no-prototype-assignment)' will be displayed. Therefore, you need to run the following code in a TS file:
88if (applePrototype === Person.prototype) {
89  hilog.info(0x0000, 'Node-API', 'get_prototype_success');
90} else {
91  hilog.info(0x0000, 'Node-API', 'get_prototype_fail');
92}
93```
94
95### napi_create_object
96
97Use **napi_create_object** to create an empty ArkTS object.
98
99CPP code:
100
101```cpp
102#include "napi/native_api.h"
103
104napi_value NewObject(napi_env env, napi_callback_info info)
105{
106    napi_value object = nullptr;
107    // Create an empty object.
108    napi_create_object(env, &object);
109    // Set the object property.
110    napi_value name = nullptr;
111    // Set the property name to "name".
112    napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &name);
113    napi_value value = nullptr;
114    // Set the property value to "Hello from N-API!"
115    napi_create_string_utf8(env, "Hello from Node-API!", NAPI_AUTO_LENGTH, &value);
116    // Set the property on the object.
117    napi_set_property(env, object, name, value);
118    return object;
119}
120```
121
122API declaration:
123
124```ts
125// index.d.ts
126export const createObject: () => { name: string };
127```
128
129ArkTS code:
130
131```ts
132import hilog from '@ohos.hilog'
133import testNapi from 'libentry.so'
134try {
135  const myObject = testNapi.createObject();
136  hilog.info(0x0000, 'testTag', 'Test Node-API napi_create_object: %{public}s', myObject.name);
137} catch (error) {
138  hilog.error(0x0000, 'testTag', 'Test Node-API napi_create_object errorCode: %{public}s, errorMessage: %{public}s', error.code, error.message);
139}
140```
141
142### napi_object_freeze
143
144Use **napi_object_freeze** to freeze an ArkTS object. After an object is frozen, new properties or methods cannot be added to the object, and the values of existing properties or methods cannot be modified.
145
146CPP code:
147
148```cpp
149#include "hilog/log.h"
150#include "napi/native_api.h"
151
152static napi_value ObjectFreeze(napi_env env, napi_callback_info info)
153{
154    // Obtain the object passed from ArkTS.
155    size_t argc = 1;
156    napi_value argv[1] = {nullptr};
157    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
158
159    // Call napi_object_freeze to freeze the object passed in.
160    napi_value objFreeze = argv[0];
161    napi_status status = napi_object_freeze(env, objFreeze);
162    if (status == napi_ok) {
163        OH_LOG_INFO(LOG_APP, "Node-API napi_object_freeze success");
164    }
165    // Return the frozen object to ArkTS.
166    return objFreeze;
167}
168```
169
170API declaration:
171
172```ts
173// index.d.ts
174export interface Obj {
175  data: number
176  message: string
177}
178export const objectFreeze: (objFreeze: Object) => Obj;
179```
180
181ArkTS code:
182
183```ts
184import hilog from '@ohos.hilog'
185import testNapi from 'libentry.so'
186try {
187  class Obj {
188    data: number = 0
189    message: string = ""
190  }
191  let obj: Obj = {data: 0, message: "hello world"};
192  let objFreeze = testNapi.objectFreeze(obj);
193  hilog.info(0x0000, 'testTag', 'Test Node-API napi_object_freeze: %{public}s', (objFreeze.data = 1));
194} catch (error) {
195  hilog.error(0x0000, 'testTag', 'Test Node-API napi_object_freeze error: %{public}s', error.message);
196}
197```
198
199### napi_object_seal
200
201Use **napi_object_seal** to seal an ArkTS object. After an object is sealed, new properties cannot be added to the object, existing properties cannot be deleted, but the values of existing properties can be modified.
202
203CPP code:
204
205```cpp
206#include "hilog/log.h"
207#include "napi/native_api.h"
208
209static napi_value ObjectSeal(napi_env env, napi_callback_info info)
210{
211    // Obtain the object passed from ArkTS.
212    size_t argc = 1;
213    napi_value argv[1] = {nullptr};
214    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
215    // Call napi_object_seal to seal the object passed in.
216    napi_value objSeal = argv[0];
217    napi_status status = napi_object_seal(env, objSeal);
218    if (status == napi_ok) {
219        OH_LOG_INFO(LOG_APP, "Node-API napi_object_seal success");
220    }
221    // Return the sealed object to ArkTS.
222    return objSeal;
223}
224```
225
226API declaration:
227
228```ts
229// index.d.ts
230export interface Obj {
231  data: number
232  message: string
233  id: number
234}
235export const objectSeal : (objSeal: Object) => Obj;
236```
237
238ArkTS code:
239
240```ts
241import hilog from '@ohos.hilog'
242import testNapi from 'libentry.so'
243try {
244  class Obj {
245    data: number = 0
246    message: string = ""
247    // Optional property.
248    address?: number = 0
249  }
250  let obj: Obj = { data: 0, message: "hello world"};
251  let objSeal = testNapi.objectSeal(obj);
252  hilog.info(0x0000, 'testTag', 'Test Node-API napi_object_seal: %{public}s', objSeal.message);
253  objSeal.data = 1;
254  hilog.info(0x0000, 'testTag', 'Test Node-API napi_object_seal: %{public}d', objSeal.data);
255  hilog.info(0x0000, 'testTag', 'Test Node-API napi_object_seal: %{public}d', (objSeal.id = 1));
256} catch (error) {
257  hilog.error(0x0000, 'testTag', 'Test Node-API napi_object_seal error: %{public}s', error.message);
258}
259```
260
261### napi_typeof
262
263Use **napi_typeof** to obtain the type of an ArkTS value.
264
265CPP code:
266
267```cpp
268#include "napi/native_api.h"
269
270static napi_value NapiTypeof(napi_env env, napi_callback_info info)
271{
272    // Obtain the parameter.
273    size_t argc = 1;
274    napi_value args[1] = {nullptr};
275    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
276
277    // Call napi_typeof to check the type of the ArkTS parameter passed in.
278    napi_valuetype valueType;
279    napi_status status = napi_typeof(env, args[0], &valueType);
280    if (status != napi_ok) {
281        napi_throw_error(env, nullptr, "Node-API napi_typeof fail");
282        return nullptr;
283    }
284    // Convert the result to napi_value and return napi_value.
285    napi_value returnValue = nullptr;
286    switch(valueType) {
287    case napi_undefined:
288        napi_create_string_utf8(env, "Input type is napi_undefined", NAPI_AUTO_LENGTH, &returnValue);
289        break;
290    case napi_null:
291        napi_create_string_utf8(env, "Input type is napi_null", NAPI_AUTO_LENGTH, &returnValue);
292        break;
293    case napi_boolean:
294        napi_create_string_utf8(env, "Input type is napi_boolean", NAPI_AUTO_LENGTH, &returnValue);
295        break;
296    case napi_number:
297        napi_create_string_utf8(env, "Input type is napi_number", NAPI_AUTO_LENGTH, &returnValue);
298        break;
299    case napi_string:
300        napi_create_string_utf8(env, "Input type is napi_string", NAPI_AUTO_LENGTH, &returnValue);
301        break;
302    case napi_object:
303        napi_create_string_utf8(env, "Input type is napi_object", NAPI_AUTO_LENGTH, &returnValue);
304        break;
305    case napi_function:
306        napi_create_string_utf8(env, "Input type is napi_function", NAPI_AUTO_LENGTH, &returnValue);
307        break;
308    case napi_bigint:
309        napi_create_string_utf8(env, "Input type is napi_bigint", NAPI_AUTO_LENGTH, &returnValue);
310        break;
311    default:
312        napi_create_string_utf8(env, "unknown", NAPI_AUTO_LENGTH, &returnValue);
313    }
314
315    return returnValue;
316}
317```
318
319API declaration:
320
321```ts
322// index.d.ts
323export const napiTypeof : <T>(value: T) => string | void;
324```
325
326ArkTS code:
327
328```ts
329import hilog from '@ohos.hilog'
330import testNapi from 'libentry.so'
331try {
332  let varUndefined: undefined;
333  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varUndefined));
334  let varNull: null = null;
335  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varNull));
336  let varTrue= true;
337  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varTrue));
338  let varNum = 1;
339  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varNum));
340  let varString = "str";
341  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varString));
342  class Obj {
343    id: number = 0
344    name: string = ""
345  }
346  let varObject: Obj = {id: 1, name: "LiLei"};
347  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varObject));
348  const addNum = (a: number, b: number): number => a * b;
349  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(addNum));
350  let varBigint = BigInt("1234567890123456789012345678901234567890");
351  hilog.info(0x0000, 'testTag', 'Test Node-API napi_typeof: %{public}s', testNapi.napiTypeof(varBigint));
352} catch (error) {
353  hilog.error(0x0000, 'testTag', 'Test Node-API napi_typeof error: %{public}s', error.message);
354}
355```
356
357### napi_instanceof
358
359Use **napi_instanceof** to check whether an ArkTS object is an instance of the specified constructor.
360
361CPP code:
362
363```cpp
364#include "napi/native_api.h"
365
366static napi_value NapiInstanceof(napi_env env, napi_callback_info info)
367{
368    // Obtain the two parameters passed from ArkTS.
369    size_t argc = 2;
370    napi_value args[2] = {nullptr};
371    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
372    // Call napi_instanceof to check whether the given object is an instance of the given constructor.
373    bool result = true;
374    napi_status status = napi_instanceof(env, args[0], args[1], &result);
375    if (status != napi_ok) {
376        napi_throw_error(env, nullptr, "Node-API napi_instanceof fail");
377        return nullptr;
378    }
379    // Convert the result to napi_value and return napi_value.
380    napi_value returnValue = nullptr;
381    napi_get_boolean(env, result, &returnValue);
382
383    return returnValue;
384}
385```
386
387API declaration:
388
389```ts
390// index.d.ts
391export const napiInstanceof: (date: Object, construct: Object) => boolean | void;
392```
393
394ArkTS code:
395
396```ts
397import hilog from '@ohos.hilog'
398import testNapi from 'libentry.so'
399try {
400  class Person {
401    name: string;
402    age: number;
403
404    constructor(name: string, age: number) {
405      this.name = name;
406      this.age = age;
407    }
408  }
409  const person = new Person("Alice", 30);
410  class Obj {
411    data: number = 0
412    message: string = ""
413  }
414  let obj: Obj = { data: 0, message: "hello world"};
415  hilog.info(0x0000, 'testTag', 'Test Node-API napi_instanceof: %{public}s', testNapi.napiInstanceof(person, Person));
416  hilog.info(0x0000, 'testTag', 'Test Node-API napi_instanceof: %{public}s', testNapi.napiInstanceof(obj, Person));
417} catch (error) {
418  hilog.error(0x0000, 'testTag', 'Test Node-API napi_instanceof error: %{public}s', error.message);
419}
420```
421
422### napi_type_tag_object
423
424Use **napi_type_tag_object** to associate the value of a **type_tag** pointer with an ArkTS object so that the object can be identified more accurately.
425
426### napi_check_object_type_tag
427
428Use **napi_check_object_type_tag** to check whether an ArkTS object is associated with a tag pointer.
429
430The type tags associate native types with ArkTS types, allowing ArkTS objects to be accurately identified and processed in C/C++.
431
432CPP code:
433
434```cpp
435#include "napi/native_api.h"
436
437#define NUMBERINT_FOUR 4
438// Define a static constant named napi_type_tag array to store type tags.
439static const napi_type_tag TagsData[NUMBERINT_FOUR] = {
440    {0x9e4b2449547061b3, 0x33999f8a6516c499},
441    {0x1d55a794c53a726d, 0x43633f509f9c944e},
442    // Default tag or no tag.
443    {0, 0},
444    {0x6a971439f5b2e5d7, 0x531dc28a7e5317c0},
445};
446
447static napi_value SetTypeTagToObject(napi_env env, napi_callback_info info)
448{
449    // Obtain the call information and parameters.
450    size_t argc = 2;
451    napi_value args[2] = {nullptr};
452    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
453    // Obtain the index number and convert it to napi_value.
454    int32_t index = 0;
455    napi_get_value_int32(env, args[1], &index);
456    // Set the type tag for the parameter (object).
457    napi_status status = napi_type_tag_object(env, args[0], &TagsData[index]);
458    if (status != napi_ok) {
459        napi_throw_error(env, "Reconnect error", "napi_type_tag_object failed");
460        return nullptr;
461    }
462    // Convert the bool value to napi_value and return it.
463    napi_value result = nullptr;
464    napi_get_boolean(env, true, &result);
465    return result;
466}
467
468static napi_value CheckObjectTypeTag(napi_env env, napi_callback_info info)
469{
470    // Obtain the call information and parameters.
471    size_t argc = 2;
472    napi_value args[2] = {nullptr};
473    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
474    // Obtain the index number and convert it to napi_value.
475    int32_t index = 0;
476    napi_get_value_int32(env, args[1], &index);
477    // Check the type tag of the object.
478    bool checkResult = true;
479    napi_check_object_type_tag(env, args[0], &TagsData[index], &checkResult);
480    // Convert the bool value to napi_value and return it.
481    napi_value checked = nullptr;
482    napi_get_boolean(env, checkResult, &checked);
483
484    return checked;
485}
486```
487
488API declaration:
489
490```ts
491// index.d.ts
492export const setTypeTagToObject: (obj: Object, index: number) => boolean | void;
493export const checkObjectTypeTag: (obj: Object, index: number) => boolean;
494```
495
496ArkTS code:
497
498```ts
499import hilog from '@ohos.hilog'
500import testNapi from 'libentry.so'
501class Obj {
502  data: number = 0
503  message: string = ""
504}
505let objA: Obj = { data: 0, message: "hello world"};
506let objB: Obj = { data: 10, message: "typetag"};
507hilog.info(0x0000, 'testTag', 'Test Node-API napi_type_tag_object objA -> 0: %{public}s', testNapi.setTypeTagToObject(objA, 0));
508hilog.info(0x0000, 'testTag', 'Test Node-API napi_type_tag_object objB -> 0: %{public}s', testNapi.setTypeTagToObject(objB, 0));
509hilog.info(0x0000, 'testTag', 'Test Node-API napi_check_object_type_tag objA -> 0: %{public}s', testNapi.checkObjectTypeTag(objA, 0));
510hilog.info(0x0000, 'testTag', 'Test Node-API napi_check_object_type_tag objB -> 1: %{public}s', testNapi.checkObjectTypeTag(objB, 1));
511```
512
513### napi_create_external
514
515Use **napi_create_external** to create an ArkTS external object wrapping a custom C/C++ object and expose it to ArkTS. With this API, you can create a Node-API value that contains a pointer to a custom C/C++ object so that the object can be accessed and managed by ArkTS.
516
517CPP code:
518
519```cpp
520#include <cstdlib>
521#include <string>
522#include "napi/native_api.h"
523
524// Callback used to release the external data.
525void finalizeCallback(napi_env env, void *data, void *hint) {
526    // Release external data.
527    free(data);
528}
529
530static napi_value GetExternalType(napi_env env, napi_callback_info info)
531{
532    size_t argc = 1;
533    napi_value args[1] = {nullptr};
534    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
535    // Obtain the data type of the parameter.
536    napi_valuetype valueType;
537    napi_typeof(env, args[0], &valueType);
538    napi_value returnValue = nullptr;
539    if (valueType == napi_external) {
540        // If the data type is napi_external, return true.
541        napi_get_boolean(env, true, &returnValue);
542    } else {
543        napi_get_boolean(env, false, &returnValue);
544    }
545    return returnValue;
546}
547
548static napi_value CreateExternal(napi_env env, napi_callback_info info)
549{
550    // Set the external data size to 10.
551    const size_t dataSize = 10;
552    // Allocate memory to the external data.
553    void *data = malloc(dataSize);
554    // Initialize the external data.
555    memset(data, 0, dataSize);
556    napi_value result = nullptr;
557    // Return an object with external data.
558    napi_status status = napi_create_external(env, data, finalizeCallback, nullptr, &result);
559    if (status != napi_ok) {
560        napi_throw_error(env, nullptr, " Node-API Failed to create external data");
561        return nullptr;
562    }
563    return result;
564}
565```
566
567API declaration:
568
569```ts
570// index.d.ts
571export const createExternal: () => Object;
572export const getExternalType: (externalData: Object) => boolean;
573```
574
575ArkTS code:
576
577```ts
578import hilog from '@ohos.hilog'
579import testNapi from 'libentry.so'
580const externalData = testNapi.createExternal();
581hilog.info(0x0000, 'testTag', 'Test Node-API napi_create_external:%{public}s', testNapi.getExternalType(externalData));
582```
583
584### napi_get_value_external
585
586Use **napi_get_value_external** to obtain the ArkTS data from the external object created by **napi_create_external**.
587
588CPP code:
589
590```cpp
591#include "napi/native_api.h"
592
593static int external = 5;
594static napi_value GetValueExternal(napi_env env, napi_callback_info info)
595{
596    // Create external data.
597    int* data = &external;
598    napi_value setExternal = nullptr;
599    napi_create_external(env, data, nullptr, nullptr, &setExternal);
600    // Obtain the value of the external data.
601    void *getExternal;
602    napi_get_value_external(env, setExternal, &getExternal);
603    // Return the obtained external data.
604    napi_value result = nullptr;
605    napi_create_int32(env, *(int *)getExternal, &result);
606    return result;
607}
608```
609
610API declaration:
611
612```ts
613// index.d.ts
614export const getValueExternal: () => number;
615```
616
617ArkTS code:
618
619```ts
620import hilog from '@ohos.hilog'
621import testNapi from 'libentry.so'
622hilog.info(0x0000, 'Node-API', 'get_value_external:%{public}d', testNapi.getValueExternal());
623```
624
625### napi_create_symbol
626
627Use **napi_create_symbol** to create a symbol. Symbol is a special data type used to indicate a unique identifier. Unlike strings or numbers, the value of a symbol is unique. Even if two symbols have the same description, they are not equal. Symbols are often used as keys for object properties to ensure property uniqueness.
628
629CPP code:
630
631```cpp
632#include "napi/native_api.h"
633
634static napi_value CreateSymbol(napi_env env, napi_callback_info info)
635{
636    napi_value result = nullptr;
637    const char *des = "only";
638    // Use napi_create_string_utf8 to create a description string.
639    napi_create_string_utf8(env, des, NAPI_AUTO_LENGTH, &result);
640    napi_value returnSymbol = nullptr;
641    // Create a symbol and return it.
642    napi_create_symbol(env, result, &returnSymbol);
643    return returnSymbol;
644}
645```
646
647API declaration:
648
649```ts
650// index.d.ts
651export const createSymbol : () => symbol;
652```
653
654ArkTS code:
655
656```ts
657import hilog from '@ohos.hilog'
658import testNapi from 'libentry.so'
659let varSymbol = testNapi.createSymbol();
660hilog.info(0x0000, 'Node-API', 'createSymbol:%{public}s', typeof varSymbol);
661```
662
663To print logs in the native CPP, add the following information to the **CMakeLists.txt** file and add the header file by using **#include "hilog/log.h"**.
664
665```text
666// CMakeLists.txt
667add_definitions( "-DLOG_DOMAIN=0xd0d0" )
668add_definitions( "-DLOG_TAG=\"testTag\"" )
669target_link_libraries(entry PUBLIC libhilog_ndk.z.so)
670```
671