前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊一下 Node.js 中 NAPI 的 napi_value

聊一下 Node.js 中 NAPI 的 napi_value

作者头像
五月君
发布2021-05-11 14:18:47
1.8K0
发布2021-05-11 14:18:47
举报
文章被收录于专栏:Nodejs技术栈

napi_value是NAPI中非常重要的数据结构,定义如下

代码语言:javascript
复制
typedef struct napi_value__* napi_value;

学过c语言的同学应该知道typedef是什么意思,他的作用就是定义类型别名。

代码语言:javascript
复制
typedef int intType;
intType a = 1;

但是我们发现搜遍Node.js的源码都找不到napi_value__定义,那这个定义是什么意思呢?c语言中,允许定义一个没有定义的结构体的指针。所以napi_value其实就是一个一级指针。他不需要类型信息,因为Node.js不会对他进行解引用。比如在c语言中有以下代码

代码语言:javascript
复制
int a = 1;int *p = &a;printf("%d", *p);

这是正确的用法,下面我们来改一下

代码语言:javascript
复制
int a = 1;void *p = &a;printf("%d", *p);

执行以上代码我们会得到以下错误信息

代码语言:javascript
复制
main.c: In function ‘main’:
main.c:7:14: warning: dereferencing ‘void *’ pointer
    7 | printf("%d", *p);
      |              ^~
main.c:7:14: error: invalid use of void expression

因为在c语言中,对指针解引用的时候,需要有类型信息,比如char、int类型,这时候才能知道要读取多少字节的内存数据。所以改成以下代码就可以了。

代码语言:javascript
复制
int a = 1;void *p = &a;printf("%d", *(int *)p);

那么Node.js中的这个定义有什么用呢?我们看看他的用法。下面以NAPI中创建一个数组的API为例。

代码语言:javascript
复制
// 创建一个数组,对应js的数组
napi_status napi_create_array(napi_env env, napi_value* result) {
  *result = v8impl::JsValueFromV8LocalValue(v8::Array::New(env->isolate));  return napi_clear_last_error(env);}

napi_create_array首先通过v8的接口v8::Array::New拿到一个数组对象。然后调用JsValueFromV8LocalValue,我们看看JsValueFromV8LocalValue的定义。

代码语言:javascript
复制
// 把v8类型转成napi类型inline napi_value JsValueFromV8LocalValue(v8::Local<v8::Value> local) {  return reinterpret_cast<napi_value>(*local);}

我们看到JsValueFromV8LocalValue返回值是napi_value类型,即一个一级指针。他保存了v8创建的对象的地址信息。我们可以先不用深究*local是什么。接着执行了

代码语言:javascript
复制
*result = napi_value变量;

result类型是napi_value*,即二级指针,这样调用方就拿到了v8创建的对象。我们看一下具体的调用代码。

代码语言:javascript
复制
napi_value ret;napi_create_array(env, &ret);

执行以上代码后,ret就保存了v8对象的信息。那么这样做有什么好处呢?我们继续看一下对ret的使用。

代码语言:javascript
复制
napi_set_element(env, ret, index, vlaue;)

接着看一下napi_set_element。

代码语言:javascript
复制
// 设置key对应的值,key是数字
napi_status napi_set_element(napi_env env,
                             napi_value object,
                             uint32_t index,
                             napi_value value) {
  v8::Local<v8::Context> context = env->context();
  v8::Local<v8::Object> obj;
  // 校验并转成对应的v8类型
  CHECK_TO_OBJECT(env, context, obj, object);
  v8::Local<v8::Value> val = v8impl::V8LocalValueFromJsValue(value);
  auto set_maybe = obj->Set(context, index, val);  return GET_RETURN_STATUS(env);}

napi_set_element中最主要的是CHECK_TO_OBJECT。

代码语言:javascript
复制
#define CHECK_TO_OBJECT(env, context, result, src) \
  CHECK_TO_TYPE((env), Object, (context), (result), (src), napi_object_expected)

是一个宏,继续展开

代码语言:javascript
复制
#define CHECK_TO_TYPE(env, type, context, result, src, status)                \
  do {                                                                        \
    CHECK_ARG((env), (src));  
    // 把napi类型转成v8类型,校验是否为空,非空则返回
                                                    \
    auto maybe = v8impl::V8LocalValueFromJsValue((src))->To##type((context)); \
    CHECK_MAYBE_EMPTY((env), maybe, (status));                                \
    (result) = maybe.ToLocalChecked();                                        \
  } while (0)

接着看V8LocalValueFromJsValue

代码语言:javascript
复制
// 把napi类型转成v8类型inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {  v8::Local<v8::Value> local;
  memcpy(static_cast<void*>(&local), &v, sizeof(v));  return local;}

V8LocalValueFromJsValue把napi_value v的值复制到local中,我们看看Local类的定义。

代码语言:javascript
复制
class Local {    T* val_;}

即把v的值复制到了val_中,后续就可以按照v8的模式去使用了。

分析到这里,就结束了,那么napi_value到底有什么用呢?napi_value其实就是暂存v8对象信息的变量,他的用处就是可以保存任意类型的v8对象,因为不管什么类型的v8对象,他的地址大小是一样的,我们只需要面对napi_value就行,不需要关注v8的对象类型,当我们调用后续接口时只需要传入napi_value,Node.js就会帮我们处理好之后(转换成对应的v8类型)再调用v8的接口,否则用户就需要这样做。

代码语言:javascript
复制
v8::Local<v8::Object> v8value = v8::Object::New(...);
v8::Local<v8::Array> v8value = v8::Array::New(...);

总的来说,napi_value让我们可以不用关注v8的东西。最后看个小例子 例子1

代码语言:javascript
复制
#include <stdio.h>typedef struct napi_value__* napi_value;int main(){   int a = 2;
   int *p = &a;
   napi_value ptr = (napi_value)p;
   printf("%d", *(int *)ptr);   return 0;}

例子2

代码语言:javascript
复制
#include <stdio.h>typedef void* napi_value;int main(){   int a = 2;
   int *p = &a;
   napi_value ptr = (napi_value)p;
   printf("%d", *(int *)ptr);   return 0;}

我们看到其实使用void *类型也是可以的。

- 这是底线 -

敬请关注「Nodejs技术栈」微信公众号,获取优质文章,如需投稿可在后台留言与我取得联系。

往期精彩推荐

为何面向手写代码常被吐槽,但其仍未动摇?

Node.js 为前端赋能 | 我要把这 200 万张页面发上线

Node.js 为前端赋能 | 如何推动基础架构项目落地

编程语言新宠 Rust 不完全入门指南

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Nodejs技术栈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档