除了上文说的全局函数,我们还可以创建类函数。
虽然我使用JS很少,但是就我感觉,函数和对象其实就是一个东西的两种表现。函数是一种特殊的对象,创建的时候,需要进行一些操作,返回的值不是函数对象,而是指定的其他东西。
希望动态创建一个对象,自然首先想到应该使用ObjectTemplate。同时,也可以发现这个类中有SetInternalFieldCount,而Object中有SetInternalField。这两个方法,就用来传递C++代码中的对象实例指针给一个JS的Object。然后再将一个FunctionTemplate添加进入ObjectTemplate。这个FunctionTemplate对应的C函数,从传入的参数中取得前面传递的对象,然后进行对应操作。其实V8仅仅维护的是一个指针,所以我传入Objective-C的类指针,访问也没什么问题。不过引用计数管理内存应该会有问题吧。暂时还找不到应该何时去调用release,就先泄露着吧。
Handle<ObjectTemplate> mars_obj_templ = ObjectTemplate::New();
mars_obj_templ->Set("backToMars", FunctionTemplate::New(backToMars));
mars_obj_templ->SetInternalFieldCount(1);
Local<Object> obj = mars_obj_templ->NewInstance();
V8Mars* mars = [[V8Mars alloc] init];
mars.name = @"earth";
obj->SetInternalField(0, External::New(mars));
context->Global()->Set(String::New("mars"), obj);
Handle<String> source = String::New("mars.backToMars();");
实现一下函数
Handle<Value> backToMars(const Arguments& args)
{
Local<Object> self = args.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
return String::New([static_cast<V8Mars*>(ptr).name cStringUsingEncoding:NSUTF8StringEncoding]);
}
这样确实我把一个对象实例同js的对象关联起来了。但是,假如我想要直接在js中创建一个新的实例,这个实例应该怎样和一个C的类实例关联呢?ObjectTemplate中有一个私有的New方法,可以传递一个构造函数,但是显然,我们不能使用。
还记得本小节开始的话么?对,这时候需要使用一个FunctionTemplate。用FunctionObject来产生一个原型Object。(话说,这个对于我还是很缠绕啊,JS本身是否就是这么设定的?)FunctionTemplate可以获取到自己对应的PrototypeTemplate,是一个ObjectTemplate。
Handle<FunctionTemplate> mars_obj_templ = FunctionTemplate::New(createMars);
Handle<ObjectTemplate> mars_obj_proto = mars_obj_templ->PrototypeTemplate();
mars_obj_proto->Set("backToMars", FunctionTemplate::New(backToMars));
Handle<ObjectTemplate> mars_obj_inst = mars_obj_templ->InstanceTemplate();
mars_obj_inst->SetInternalFieldCount(1);
Handle<Function> mars_obj_ctor = mars_obj_templ->GetFunction();
context->Global()->Set(String::New("Mars"), mars_obj_ctor);
Handle<String> source = String::New("var mars = new Mars(); mars.backToMars();");
我试图调整一下proto设置类函数的代码位置,也就是第三行的代码到后面去,结果就显示创建的对象中没有backToMars这个方法。我猜测,一旦对一个原型进行了修改,就会产生一个新的原型,而以前的方法,就是obj_templ->GetFunction的到的函数是无法访问新产生的原型。
这样,总算是可以了。贴上这次的代码
@interface V8Mars : NSObject {
@private
NSString *name;
}
@property (nonatomic, retain)NSString *name;
@end
@implementation V8Mars
@synthesize name;
- (void)dealloc
{
[self.name release];
[super dealloc];
}
- (id)init
{
self = [super init];
if (self != nil)
{
self.name = @"ground";
}
return self;
}
@end
Handle<Value> backToMars(const Arguments& args)
{
Local<Object> self = args.Holder();
Local<External> wrap = Local<External>::Cast(self->GetInternalField(0));
void* ptr = wrap->Value();
return String::New([static_cast<V8Mars*>(ptr).name cStringUsingEncoding:NSUTF8StringEncoding]);
}
Handle<Value> createMars(const Arguments& args)
{
Local<Object> self = args.Holder();
V8Mars* mars = [[V8Mars alloc] init];
mars.name = @"Really mars";
self->SetInternalField(0, External::New(mars));
return self;
}
- (void)releateClass
{
HandleScope handle_scope;
Persistent<Context> context = Context::New();
Context::Scope context_scope(context);
Handle<FunctionTemplate> mars_obj_templ = FunctionTemplate::New(createMars);
Handle<ObjectTemplate> mars_obj_proto = mars_obj_templ->PrototypeTemplate();
mars_obj_proto->Set("backToMars", FunctionTemplate::New(backToMars));
Handle<ObjectTemplate> mars_obj_inst = mars_obj_templ->InstanceTemplate();
mars_obj_inst->SetInternalFieldCount(1);
Handle<Function> mars_obj_ctor = mars_obj_templ->GetFunction();
context->Global()->Set(String::New("Mars"), mars_obj_ctor);
Handle<String> source = String::New("var mars = new Mars(); mars.backToMars();");
Handle<Script> script = Script::Compile(source);
Handle<Value> result = script->Run();
String::Utf8Value utf8Result(result);
NSLog(@"%@", [NSString stringWithCString:*utf8Result encoding:NSUTF8StringEncoding]);
context.Dispose();
}