V8 嵌入:class 中的方法 null `this` 继承自使用 Function::New 创建的方法
V8 embedding: null `this` from method inside class inheriting from one created using Function::New
我正在尝试使用自定义 class 嵌入 V8。它是为了在 JS 中扩展和使用。
在构造函数以外的方法中访问this
时,为null
。它应该能够为 class.
的那个实例保留数据
我是不是漏掉了什么?
JavaScript
import { Behavior } from '@giz/ecs';
class A extends Behavior {
constructor(a) {
super(a);
this.a = 10;
print(this); // this works
}
update() {
print(this); // null
}
}
A;
我是如何创作的Behavior
Local<Module> ecsModule = Module::CreateSyntheticModule(
isolate,
String::NewFromUtf8(isolate, "@giz/ecs").ToLocalChecked(),
{String::NewFromUtf8(isolate, "Behavior").ToLocalChecked()},
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Value>
{
auto isolate = context->GetIsolate();
// behavior constructor right now is an empty function
auto behavior = Function::New(context, behaviorConstructor).ToLocalChecked();
module->SetSyntheticModuleExport(
String::NewFromUtf8(isolate, "Behavior").ToLocalChecked(),
behavior);
return MaybeLocal<Value>(True(isolate));
});
ecsModule->InstantiateModule(context, ScriptingSystem::moduleResolutionCallback);
// store the module for later use
Global<Module> *globalModule = new Global<Module>();
globalModule->Reset(isolate, ecsModule);
modules["@giz/ecs"] = globalModule;
实例化class并调用更新函数
v8::HandleScope handle_scope(isolate);
// globalContext is a v8::Global<v8::Context> with the print function created
v8::Local<Context> context = Local<Context>::New(isolate, globalContext);
Context::Scope contextScope(context);
// ...
// create script origin, script source, instantiate module, ..
// ...
Local<Value> returnValue = module->Evaluate(context).ToLocalChecked();
Local<Object> returnedBehavior = returnValue->ToObject(context).ToLocalChecked();
Local<Value> arguments[1];
arguments[0] = wrapEntity(*behavior->entity);
// creates an instance of the behavior
auto instance = returnedBehavior->CallAsConstructor(context, 1, arguments)
.ToLocalChecked()
->ToObject(context)
.ToLocalChecked();
// calls the update function
instance->Get(context, String::NewFromUtf8(isolate, "update").ToLocalChecked())
.ToLocalChecked()
->ToObject(context)
.ToLocalChecked()
->CallAsFunction(context, Null(isolate), 0, nullptr);
谢谢。
问题在最后一行:
->CallAsFunction(context, Null(isolate), 0, nullptr);
CallAsFunction
的第二个参数是“接收者”,它是在被调用函数中可以作为 this
访问的东西:因为你在那里传递 Null(isolate)
,this
将是 null
。尝试传递 instance
。
(本质上,您当前的代码相当于 let instance = new A(); instance.update.call(null);
。)
旁注:请注意,如果之前的操作抛出异常,ToLocalChecked
将使您的进程崩溃。将 MaybeLocal
s 显式转换为 Local
s 的要求已被引入,目的是指出 C++ 代码应检查异常并以某种方式处理它们的所有位置。只有在您可以保证操作不会抛出的极少数情况下(例如分配一个短字符串),使用 ToLocalChecked
才是安全的。当前会使您的进程崩溃的示例:
A
的构造函数抛出
A
的构造函数returnsnull
A
的构造函数为 update
安装一个 getter 抛出
A
的构造函数为 update
安装一个 getter,returns null
一个常见的模式是:
Local<...> value;
if (!FunctionThatReturnsMaybeLocal(...).ToLocal(&value)) {
/* Handle error: show message, skip silently, ... */
return;
}
// Now work with {value}.
(是的,从 C++ 使用 JavaScript 很困难。)
我正在尝试使用自定义 class 嵌入 V8。它是为了在 JS 中扩展和使用。
在构造函数以外的方法中访问this
时,为null
。它应该能够为 class.
我是不是漏掉了什么?
JavaScript
import { Behavior } from '@giz/ecs';
class A extends Behavior {
constructor(a) {
super(a);
this.a = 10;
print(this); // this works
}
update() {
print(this); // null
}
}
A;
我是如何创作的Behavior
Local<Module> ecsModule = Module::CreateSyntheticModule(
isolate,
String::NewFromUtf8(isolate, "@giz/ecs").ToLocalChecked(),
{String::NewFromUtf8(isolate, "Behavior").ToLocalChecked()},
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Value>
{
auto isolate = context->GetIsolate();
// behavior constructor right now is an empty function
auto behavior = Function::New(context, behaviorConstructor).ToLocalChecked();
module->SetSyntheticModuleExport(
String::NewFromUtf8(isolate, "Behavior").ToLocalChecked(),
behavior);
return MaybeLocal<Value>(True(isolate));
});
ecsModule->InstantiateModule(context, ScriptingSystem::moduleResolutionCallback);
// store the module for later use
Global<Module> *globalModule = new Global<Module>();
globalModule->Reset(isolate, ecsModule);
modules["@giz/ecs"] = globalModule;
实例化class并调用更新函数
v8::HandleScope handle_scope(isolate);
// globalContext is a v8::Global<v8::Context> with the print function created
v8::Local<Context> context = Local<Context>::New(isolate, globalContext);
Context::Scope contextScope(context);
// ...
// create script origin, script source, instantiate module, ..
// ...
Local<Value> returnValue = module->Evaluate(context).ToLocalChecked();
Local<Object> returnedBehavior = returnValue->ToObject(context).ToLocalChecked();
Local<Value> arguments[1];
arguments[0] = wrapEntity(*behavior->entity);
// creates an instance of the behavior
auto instance = returnedBehavior->CallAsConstructor(context, 1, arguments)
.ToLocalChecked()
->ToObject(context)
.ToLocalChecked();
// calls the update function
instance->Get(context, String::NewFromUtf8(isolate, "update").ToLocalChecked())
.ToLocalChecked()
->ToObject(context)
.ToLocalChecked()
->CallAsFunction(context, Null(isolate), 0, nullptr);
谢谢。
问题在最后一行:
->CallAsFunction(context, Null(isolate), 0, nullptr);
CallAsFunction
的第二个参数是“接收者”,它是在被调用函数中可以作为 this
访问的东西:因为你在那里传递 Null(isolate)
,this
将是 null
。尝试传递 instance
。
(本质上,您当前的代码相当于 let instance = new A(); instance.update.call(null);
。)
旁注:请注意,如果之前的操作抛出异常,ToLocalChecked
将使您的进程崩溃。将 MaybeLocal
s 显式转换为 Local
s 的要求已被引入,目的是指出 C++ 代码应检查异常并以某种方式处理它们的所有位置。只有在您可以保证操作不会抛出的极少数情况下(例如分配一个短字符串),使用 ToLocalChecked
才是安全的。当前会使您的进程崩溃的示例:
A
的构造函数抛出A
的构造函数returnsnull
A
的构造函数为update
安装一个 getter 抛出A
的构造函数为update
安装一个 getter,returnsnull
一个常见的模式是:
Local<...> value;
if (!FunctionThatReturnsMaybeLocal(...).ToLocal(&value)) {
/* Handle error: show message, skip silently, ... */
return;
}
// Now work with {value}.
(是的,从 C++ 使用 JavaScript 很困难。)