javasript与c&c++的交互
2011-08-14 10:16
405 查看
相信初学javascript的工作于嵌入式系统的人,当然包括我自己,都有一个疑问,如果应用是html/css/javascrip写,而中间件是c/c++写,那么javascript与c/c++中间件API是如何相互调用的呢?通过一段时间的学习,在此做个总结
JavaScriptCore API: http://developer.apple.com/library/mac/#documentation/Carbon/Reference/WebKit_JavaScriptCore_Ref/JSObjectRef_h/index.html
这是个函数名的数组和对应的c/c++函数,它在javascript中相当于:
JSStaticFunction的声明是:
name:一个字符串,表示属性的名字;
callAsFunction:
一个callback函数,即当name作为一个函数调用时,callAsFunction会被调用。可以理解为name与callAsFunction的绑定,name对javascript可见,当javascript中调用name时,即相当于callAsFunction被调用。
attributes:name的一个属性集
下面介绍下函数的实现,在c++中每个函数都有一个的声明,参数都是一致的:
finalize方法是可选的,你可以通过它在javascript类清除之前做一些资源释放之类的动作
现在全局变量$BaseTv对HTML里的javascript是可见的,你可以直接以名字$BaseTv或者window[“$BaseTv”]来使用,例如下面这段javascript代码调用函数startChannelScan
再看下面这个函数:
然后你可以在javascript中调用
现在servers就是一个对象数组,包含了属性id,name,desc
另一种方法是你可以通过javascript注册一个函数,存储它,需要的时候调用它。下面是一个例子:
在javascript中相应的定义了:
需要的时候,在c/c++中你可以用下面的方法调用msgHanlder.handle
JSStringCreateWithUTF8CString, JSStringCreateWithCFString需要用JSStringRelease释放;
JSObjectCopyPropertyNames(一个对象属性数组)需要用JSPropertyNameArrayRelease释放;
JSCreateClass需要JSClassRelease释放;
由JSGlobalContextCreate创建的Javascript context本身需要用JSGlobalContextRelease释放.
对于什么时候该主动调用release函数,什么时候GC会自动release,可能会觉得有点混乱。可以简单地这样理解,由javascriptcontext设置或创建的对象,GC会自动回收。例如:
n不是由context创建,所以需要主动回收,ret由ctx创建,GC会负责回收,在这里不需要主动调用release函数释放。
如果不想GC回收一个对象,可以使用函数JSValueProtect(JSContextRefctx, JSValueRef value)保护value不被GC回收,函授JSValueUnProtect释放保护,GC就可以回收value了,JSValueProtect与JSValueUnProtect调用的次数必需相等。
1.JavaScriptCore与头文件
基于webkit的开发需要包含下面两个头文件:#include <WebKit.h> #include <JavaScriptCore/JavaScript.h>
JavaScriptCore API: http://developer.apple.com/library/mac/#documentation/Carbon/Reference/WebKit_JavaScriptCore_Ref/JSObjectRef_h/index.html
2.在c++中创建javascript类
如果你熟悉javascript类,那么你会看到在c++中创建javascript类与在javascript里创建类是基本一样的,除了在c++中需要写成员函数,还可以有一个finalize函数,这个函数在javascript中是没有的。2.1定义一个javascript类
static JSClassDefinition jsBaseTvDefinition = { 0, //version kJSClassAttributeNone, //attributes "__BaseTvClass", //className 0, //parentClass 0, //staticValues jsBaseTvFunctions, //staticFunctions 0, //Initialize finalize, //Finalize 0, //has Property 0, //get Property 0, //set Property 0, //delete Property 0, //getPropertyNames 0, //callAsFunction 0, //hasInstance 0, //callAsConstructor 0 //convertToType }这里我定义了一个叫"__BaseTvClass"的类和一个叫finalize的方法(前缀"__"不是必需的,只是为了防止与javascript中定义的类冲突)。在javascript中,它相当于下面这个样子
function __BaseTvClass { ... }
2.2定义成员函数
static JSStaticFunction jsBaseTvFunctions[] { {"setSource",JSBaseTv::setSource,kJSPropertyAttributeNone}, {"getCurrentSource",JSBaseTv::getCurrentSource,kJSPropertyAttributeNone}, {"startChannelScan",JSBaseTv::startChannelScan,kJSPropertyAttributeNone}, {"abortChannelScan",JSBaseTv::abortChannelScan,kJSPropertyAttributeNone}, {0,0,0} }
这是个函数名的数组和对应的c/c++函数,它在javascript中相当于:
function __BaseTvClass() { this.setSource=function() { } this.getCurrentSource=function() { } this.startChannelScan=function() { } this.abortChannelScan=function() { } }
JSStaticFunction的声明是:
typedef struct { const char *const name; JSObjectCallAsFunctionCallback callAsFunction; JSPropertyAttributes attributes; } JSStaticFunction
name:一个字符串,表示属性的名字;
callAsFunction:
一个callback函数,即当name作为一个函数调用时,callAsFunction会被调用。可以理解为name与callAsFunction的绑定,name对javascript可见,当javascript中调用name时,即相当于callAsFunction被调用。
attributes:name的一个属性集
下面介绍下函数的实现,在c++中每个函数都有一个的声明,参数都是一致的:
JSValueRef JSBaseTv::startChannelScan(JSContextRef ctx,JSObjectRef function,JSObjectRef thisObject,size_t argumentCount, const JSValueRef arguments[],JSValueRef *exception) { if(argumentCount < 1) return JSValueMakeNull(ctx); JSStringRef jsStr = JSValueToStringCopy(ctx,arguments[0],NULL); char scanType[32]; JSStringGetUTF8CString(jsStr,scanType,sizeof(scanType)-1); if(!strcmp(scanType,"atv")) { /* */ } else if(!strcmp(scanType,"dtv")) { /* */ } else { /* */ } return JSValueMakeNull(ctx); }
JSValueRefJSBaseTv::abortChannelScan(JSContextRef ctx,JSObjectRef function,JSObjectRefthisObject,size_t argumentCount,const JSValueRef arguments[],JSValueRef*exception){/*argumentCount代表了传入参数的个数,arguments[]存储了具体的参数,arguments[0]是第一个参数,arguments[1]是第二个参数,以此类推*/}你可以看到每个函数参数的样式一样的,都是5个参数。
finalize方法是可选的,你可以通过它在javascript类清除之前做一些资源释放之类的动作
void JSBaseTv::finalize(JSObjectRef thisObject) { // delete me; }
3.创建javascript类的对象实例
通过上面的代码我们定义了一个c++的类和对应的一些函数,下面介绍怎么创建它的实例,从而在javasript中能够调用它。void JSBaseTv::Setup() { globalContext = JSGlobalContextCreate(0); JSObjectRef global = JSContextGetGlobalOjbect(globalContext); JSClassRef baseTvClass = JSClassCreate(&jsBaseTvDefinition); JSOjbectRef baseTv = JSObjectMake(globalContext,baseTvClass,NULL); JSStringRef name = JSStringCreateWithUTF8CString("$BaseTv"); JSObjectSetProperty(globalContext,global,name,baseTv,kJSPropertyAttributeNone,0); JSStringRelease(name); JSClassRelease(baseTvClass); }这段代码创建了一个jsBaseTvDefinition的类,并且创建了一个全局变量$BaseTv,"$"不是必需的。$BaseTv实际上就是jsBaseTvDefinition的实例化,放在javascript中,它相当的作用:
$BaseTv = new jsBaseTvDefinition();
现在全局变量$BaseTv对HTML里的javascript是可见的,你可以直接以名字$BaseTv或者window[“$BaseTv”]来使用,例如下面这段javascript代码调用函数startChannelScan
startChannelScan:function() { $BaseTv.startChannelScan("atv"); }
再看下面这个函数:
JSValueRef JSBaseTv::getDevices(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { DLNA* me = (DLNA*) JSObjectGetPrivate(thisObject); if (me->devices.size() == 0) return JSValueMakeNull(ctx); Lock lock(&me->mutex); JSObjectRef* js = new JSObjectRef[me->devices.size()]; size_t i = 0; for (map<string, DeviceHandle>::iterator it = me->devices.begin(); it != me->devices.end(); i++, it++) { JSObjectRef o = JSObjectMake(ctx, NULL, NULL); DeviceInfo info; GetDeviceInfo(me->dmp, it->second, &info); setProperty(ctx, o, "id", info.udn); setProperty(ctx, o, "name", info.friendlyName); setProperty(ctx, o, "desc", info.modelDescription); js[i] = o; } JSObjectRef r = JSObjectMakeArray(ctx, i, js, exception); delete[] js; return r; } void JSBaseTv::setProperty(JSContextRef ctx, JSObjectRef o, const char* name, const char* value) { JSStringRef n = JSStringCreateWithUTF8CString(name); JSStringRef v = JSStringCreateWithUTF8CString(value); JSObjectSetProperty(ctx, o, n, JSValueMakeString(ctx, v), kJSPropertyAttributeNone, 0); JSStringRelease(n); // JSStringRelease(v); // Do not release v as it's still needed by JSValue. It'll be released by GC. } void JSBaseTv::setProperty(JSContextRef ctx, JSObjectRef o, const char* name, double value) { JSStringRef n = JSStringCreateWithUTF8CString(name); JSObjectSetProperty(ctx, o, n, JSValueMakeNumber(ctx, value), kJSPropertyAttributeNone, 0); JSStringRelease(n); }
然后你可以在javascript中调用
var servers=$BaseTv.GetDevices()
现在servers就是一个对象数组,包含了属性id,name,desc
[{ id: "…", name: "…", desc: "…" }, …];
4.从c/c++调用javascript函数
前面介绍了如何定义一个类,并且从javascript中调用它,但一个完整的应用可能需要从c/c++中调用javascript的函数,比如丢一些事件给javascript。其实比较简单,如果有一个你想调用的全局函数并且你知道它的名字和参数,你可以通过JSObjectGetProperty获取这个函数的句柄,然后通过JSCallAsFunction调用它。另一种方法是你可以通过javascript注册一个函数,存储它,需要的时候调用它。下面是一个例子:
JSValueRef JSBaseTv::info(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception) { if (argumentCount < 1) { *exception = JSValueMakeString(ctx, JSStringCreateWithUTF8CString("Info(callback) argument invalid.")); return JSValueMakeNull(ctx); } DLNA* me = (DLNA*) JSObjectGetPrivate(thisObject); JSObjectRef cb = const_cast<JSObjectRef>(arguments[0]); if (!JSObjectIsFunction(ctx, cb)) { me->infoCallback = NULL; return JSValueMakeNull(ctx); } me->infoCallback = cb; return JSValueMakeNull(ctx); }在javascript中,你可以调用
$BaseTv.info(msgHandler.handle);handle这个函数就注册下去了。
在javascript中相应的定义了:
var msgHandler = function() { this.handle = function(msg, err) { … }; }
需要的时候,在c/c++中你可以用下面的方法调用msgHanlder.handle
void JSBaseTv::msg(const char* msg, int id) { if (!infoCallback) return; JSValueRef arguments[2]; JSStringRef n = JSStringCreateWithUTF8CString(msg); arguments[0] = JSValueMakeString(globalContext, n); arguments[1] = JSValueMakeNumber(globalContext, id); JSObjectCallAsFunction(globalContext, infoCallback, NULL, 2, arguments, NULL); JSStringRelease(n); }
5.javascript对象的生命周期
JavascriptCore的GC(Garbage Collection)会负责回收你创建的对象,GC会监视任何由JSValueMakeNumber,JSValueMakeString, JSObjectMake返回的结果,如果不用的时候GC会回收它,有几种情况例外:JSStringCreateWithUTF8CString, JSStringCreateWithCFString需要用JSStringRelease释放;
JSObjectCopyPropertyNames(一个对象属性数组)需要用JSPropertyNameArrayRelease释放;
JSCreateClass需要JSClassRelease释放;
由JSGlobalContextCreate创建的Javascript context本身需要用JSGlobalContextRelease释放.
对于什么时候该主动调用release函数,什么时候GC会自动release,可能会觉得有点混乱。可以简单地这样理解,由javascriptcontext设置或创建的对象,GC会自动回收。例如:
JSStringRef n = JSStringCreateWithUTF8CString(name); JSValueRef ret =JSObjectGetProperty(ctx, o, n, NULL); JSStringRelease(n);
n不是由context创建,所以需要主动回收,ret由ctx创建,GC会负责回收,在这里不需要主动调用release函数释放。
如果不想GC回收一个对象,可以使用函数JSValueProtect(JSContextRefctx, JSValueRef value)保护value不被GC回收,函授JSValueUnProtect释放保护,GC就可以回收value了,JSValueProtect与JSValueUnProtect调用的次数必需相等。
相关文章推荐
- Javasript&Jquary网上文摘
- Lua & C 交互 3 c/c++调用lua
- c++&lua交互
- 调试javasript 报错 "http://www.baidu.com/s?word=:&tn=s001_dg"
- Qt:QML:QML于C++交互之信号与槽(signal&slot )
- QML于C++交互之信号与槽(signal&slot )
- <转载> JavaSript模块规范 - AMD规范与CMD规范介绍
- [c&cpp] C\C++交互—extern “C”
- C++ Primer 笔记6
- C++ 日期 & 时间
- C++ Primer 笔记15
- C++ 基础之 "模版函数","类模版"
- LINUX c++线程池框架
- c++ 头文件 尖括号<> 和双引号“”的区别
- c/c++的&、异或、~、!等运算
- 【C++】Accessor and Mutator Functions & 函数形参与类私有成员重名的解决方法
- 交互细节分析——注册&登录
- V8引擎javascript与C++交互
- C++中引用(&)的用法和应用实例 - blue—— - 博客园
- 编程开发(C/C++&Java&Python&JavaScript&Go&PHP&Ruby&Perl&R&Erlang)