了解节点插件 API (N-API) HandleScope

jro*_*roy 5 v8 nan node.js node.js-addon

我很难理解如何正确使用HandleScopeEscapeableHandleScope。例如,从这个节点示例

MyObject::MyObject(const Napi::CallbackInfo& info) : Napi::ObjectWrap<MyObject>(info) {
  Napi::Env env = info.Env();
  Napi::HandleScope scope(env);

  this->val_ = info[0].As<Napi::Number>().DoubleValue();
};
Run Code Online (Sandbox Code Playgroud)

为什么在这种情况下我们需要创建一个新的 HandleScope?从另一个例子来看:

Napi::Object CreateObject(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  Napi::Object obj = Napi::Object::New(env);
  obj.Set(Napi::String::New(env, "msg"), info[0].ToString());

  return obj;
}
Run Code Online (Sandbox Code Playgroud)

为什么这里不需要呢?

另外,我没有找到任何使用 EscapeableHandleScope 的示例,什么时候需要这个?

jmr*_*mrk 2

有关 HandleScope 是什么以及使用它们的用途的说明,请参阅V8 的文档,例如该类Local

有两种类型的句柄:本地句柄和持久句柄。

本地句柄重量轻且瞬态,通常用于本地操作。它们由 HandleScopes 管理。这意味着 HandleScope 在创建时必须存在于堆栈中,并且它们仅在创建期间在 HandleScope 活动内部有效。为了将本地句柄传递给外部 HandleScope,必须使用 EscapeableHandleScope 及其 Escape() 方法。

对于班级HandleScope

管理多个本地句柄的堆栈分配类。创建句柄作用域后,所有本地句柄都将在该句柄作用域内分配,直到删除该句柄作用域或创建另一个句柄作用域。如果已经存在一个句柄作用域并且创建了一个新的句柄作用域,则所有分配都将在新的句柄作用域中进行,直到它被删除。之后,新的句柄将再次在原始句柄范围内分配。

删除本地句柄的句柄范围后,垃圾收集器将不再跟踪存储在句柄中的对象,并可能会释放它。访问已删除句柄范围的句柄的行为是未定义的。

务实地说:

  • HandleScope从 JavaScript 调用 C++ 时,如果 C++ 代码创建任何s,则至少需要一个 s Local<>。通常,1HandleScope就是正确的数字。
  • 创建和销毁 HandleScope 是有成本的,因此如果您有许多细粒度的 HandleScope,那么您就是在浪费时间。另一方面,HandleScope(根据设计,这就是它的目的!)使包含在其中的句柄所引用的所有对象保持活动状态(在 GC 意义上),因此对于非常长时间运行的代码或具有多次迭代的循环,您可能想引入短暂的 HandleScopes,以便可以释放您用完的临时对象。
  • 正如文档所述,EscapableHandleScope如果要返回超出作用域生命周期结束的对象,则需要 an 。

  • 但OP的问题具体是关于Node Addon API和Napi::HandleScope,而不是关于V8和v8::HandleScope。 (2认同)