在公司一直负责一个界面引擎的模块的设计开发工作,主要使用XML和JavaScript两种技术。

最初我们使用的是MS的JS引擎(由IActiveScript及一些相关接口组成,下称MS-JS),后来由于一些问题,将MS-JS替换成Google才推出不久的JS引擎V8(下称V8-JS)。

MS-JS不知道多久没更新了,不过也还好用,文档写得也清楚。

V8-JS是Google的Chrome浏览器中的一个亮点,刚推出不久,还在不断更新中,性能据说比MS的高过不止一个数量级,还有很重要的一点是它开源,“源码之前,了无秘密”,不过要揭示这里面的秘密,也是要精通不少技术的,比如汇编语言、编译原理、C++模板、设计模式等等,这也难怪,毕竟这是个虚拟机嘛。本人所知也是略知皮毛,在项目中有了一些使用经验,和大家分享一下。

两者设计理念上有一些差异。MS-JS以COM接口的形式发布,引擎建立在IDispatch接口的基础之上,虽然效率上要吃些亏,但是实现了在二进制代码级别上的重用,以至于我们能到处看到MS-JS的身影,在IE浏览器中,在Windows Script Host中等等。我们还可以在各种不同的编程语言中使用它(COM的确是软件重用的一个成功典范)。V8-JS以C++库的形式发布,大概是因为V8-JS对性能的要求很高,而且主要在Chrome中的使用的缘故吧。

作为Javascript的运行环境,两者在使用方式大体上是相同的:宿主程序把自己的对象与JS对象绑定,解释Js脚本并执行,脚本代码在被执行的过程中访问对象的属性和方法,当宿主程序有事件发生时可调用脚本中相应的处理代码。在细节上两者的差别就比较大了,下面分别做些简短的讲述:

1.创建JS引擎(执行上下文)

MS-JS:

//用COM函数CoCreateInstance创建引擎对象
CComPtr<IActiveScript> activeScript;
//创建脚本运行环境
HRESULT hr = ::CoCreateInstance(CLSID_JAVAScript, NULL, CLSCTX_INPROC, IID_IActiveScript, (void**)&activeScript);//创建宿主对象,应用程序需实现IActiveScriptSite,通过IActiveScript的接口函数SetScriptSite与之关联,在第一次执
//行脚本的时候,引擎会向宿主接口查询名字对象的接口。
CComPtr<IActiveScriptSite> pSite = GetScriptSite();//需实现
hr = activeScript->SetScriptSite(pSite);
//添加全局名字对象
AddNameItemToSrcipt(activeScript);//需实现

V8-JS:

//创建v8执行上下文
jsContext = Context::New();
//获得全局对象
Handle<Object> global = jsContext->Global();//添加名字对象 
AddObjectToSrcipt(global);

2.加载脚本

MS-JS:

//获取解析器
CComPtr<IActiveScriptParse> srciptParser;
hr = m_pActiveScript->QueryInterface(IID_IActiveScriptParse, (void**)&srciptParser);
//初始化脚本解析器
hr = srciptParser->InitNew();//解析脚本
hr = srciptParser->ParseScriptText(bstrScript, NULL, NULL, 
        NULL, 0, 0, SCRIPTTEXT_ISVISIBLE, NULL, NULL);

V8-JS:

//设置当前运行上下文
Context::Scope contextScope(jsContext);//捕获异常
TryCatch tryCatch;//编译脚本
Handle<String> scriptString = CStringToString(bstrScript);
Handle<String> scriptFileName = CStringToString(bstrScriptName);
Handle<Script> script = Script::Compile(scriptString, scriptFileName);

3.运行脚本

MS-JS:

//设置脚本引擎为SCRIPTSTATE_CONNECTED
hr = pActiveScript->SetScriptState(SCRIPTSTATE_CONNECTED);

V8-JS:

//设置当前运行上下文
Context::Scope contextScope(jsContext);//运行
script->Run();

4.调用脚本函数

MS-JS:

//获得全局对象
CComPtr<IDispatch> dispGlobal;
hr = m_pActiveScript->GetScriptDispatch(NULL, &dispGlobal);
//查询函数
DISPID funcID = -1;
OLECHAR *rgszNames[] = {bstrFuncName};
hr = dispGlobal->GetIDsOfNames(IID_NULL, rgszNames, 1, 0, &funcID);
//调用函数
DISPPARAMS dispParams = {NULL, 0, 0, 0};//准备参数列表
hr = dispGlobal->Invoke(funcID, IID_NULL, 0, DISPATCH_METHOD, pDispParams, pVarResult, NULL, NULL);

V8-JS:

//设置当前上下文
Context::Scope contextScope(jsContext);//捕获异常
TryCatch tryCatch;
//获得函数对象
Handle<Object> global = m_jsContext->Global();
Handle<String> funcName = CStringToString(bstrFuncName);
Handle<Value> funcValue = global->Get(funcName);
Handle<Function> func = Handle<Function>::Cast(funcValue);
//准备参数
const UINT argNum = (pDispParams != NULL) ? (pDispParams->cArgs) : (0);
Handle<Value> *vals = new Handle<Value>[argNum];
for (UINT i=0; i<argNum; i++) {
   vals[i] = ToValue(pDispParams->rgvarg[argNum-i-1]);
}//调用
Handle<Value> result = func->Call(global, argNum, vals);

两种JS引擎的基本使用方法就先介绍到这里,在下一篇将介绍一下V8引擎的回调机制