本篇内容介绍了“在C++中怎么执行JavaScript程序”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
SpiderMonkey是Mozilla项目的一部分,用C语言写成,是负责执行JavaScript脚本的引擎。另外还有一个叫Rhino的Java引擎。
SpiderMonkey的最新版本可在这里下载。它是以源代码形式发布的,因此你必须自己编译它。Visual C++用户可以在src目录下找到Workspace项目工程文件来编译,编译结果会产生一个叫'js32.dll'的dll文件。
SpiderMonkey也可以在Macintosh和Unix上使用。
初始化一个JavaScript runtime可用JS_NewRuntime方法,该方法将为runtime分配内存,同时还得指定一个字节数,当内存分配超过这个数字时垃圾收集器会自动运行。
JSRuntime *rt = JS_NewRuntime(1000000L);
if ( rt == NULL )
{
// Do some error reporting
}
Context指明了脚本运行所需的栈大小,即分配给脚本执行栈的私有内存数量。每个脚本都和它自己的context相关联。
当一个context正在被某个脚本或线程使用时,其他脚本或线程不能使用该context。不过在脚本或线程结束时,该context可以被下一个脚本或线程重用。
创建一个新context可用JS_NewContext方法。context必须关联到一个runtime,调用JS_NewContext方法时还必须指定栈的大小。
JSContext *cx = JS_NewContext(m_rt, 8192);
if ( cx == NULL )
{
// Do some error reporting
}
在一个脚本开始运行前,必须初始化一些大多数脚本会用到的通用的JavaScript函数和内置(build-in)类对象。
全局对象是在一个JSClass结构中描述的。该结构可以按以下方式初始化:
JSClass globalClass =
{
"Global", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub
};
现在创建和初始化这个全局对象:JSObject *globalObj = JS_NewObject(cx, &globalClass, 0, 0); JS_InitStandardClasses(cx, globalObj);
执行脚本的一种途径是使用JS_EvaluateScript方法:
std::string script = "var today = Date(); today.toString();"
jsval rval;
uintN lineno = 0;
JSBool ok = JS_EvaluateScript(cx, globalObj, script.c_str(),
script.length(), "script", lineno, &rval);
在这个脚本中,如果运行正确的话当天数据会保存在rval中。rval包含最后一个执行函数的结果。JS_EvaluteScript返回JS_TRUE代表执行成功,返回JS_FALSE则代表有错误发生。
从rval得到相应的字符串值可以用下面的方法。在这里我不想解释所有细节,想获得更详细的信息请自己查API文档。
JSString *str = JS_ValueToString(cx, rval);
std::cout << JS_GetStringBytes(str);
程序结束前必须对脚本引擎做一些清理工作:JS_DestroyContext(cx); JS_DestroyRuntime(rt);
这个例子中用到的类定义如下:
class Customer
{
public:
int GetAge() { return m_age; }
void SetAge(int newAge) { m_age = newAge; }
std::string GetName() { return m_name; }
void SetName(std::string newName) { m_name = newName; }
private:
int m_age;
std::string m_name;
};
从Customer类派生一个你想在JavaScript中用的新的C++类,或者创建一个包含一个Customer类型成员变量的新类。
给JavaScript用的类得有一个JSClass结构,为此得创建一个JSClass类型的静态成员变量,该变量会被其他类用到,因此还得把它声明为public变量。别的类可以用该结构来判断对象的类型(见JS_InstanceOf API)。
// JSCustomer.h
class JSCustomer
{
public:
JSCustomer() : m_pCustomer(NULL)
{
}
~JSCustomer()
{
delete m_pCustomer;
m_pCustomer = NULL;
}
static JSClass customerClass;
protected:
void setCustomer(Customer *customer)
{
m_pCustomer = customer;
}
Customer* getCustomer()
{
return m_pCustomer;
}
private:
Customer *m_pCustomer;
};
该JSClass结构里包含了JavaScript类的名字、标志位以及给脚本引擎用的回调函数的名字。举个例子,脚本引擎使用回调函数从类中获取某个属性值。
在C++类的实现文件中定义JSClass结构如下:
// JSCustomer.cpp
JSClass JSCustomer::customerClass =
{
"Customer", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JSCustomer::JSGetProperty, JSCustomer::JSSetProperty,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JSCustomer::JSDestructor
};
用到的回调函数是JSCustomer::JSGetProperty,JSCustomer::JSSetProperty和JSCustomer::JSDestructor。脚本引擎调用JSGetProperty获取属性值,调用JSSetProperty设置属性值,调用JSDestructor析构JavaScript对象。
JSCLASS_HAS_PRIVATE标志位会让脚本引擎分配一些内存,这样你可以在JavaScript对象中附加一些自定义数据,比如可以用它来保存类指针。
回调函数以C++的类静态成员函数方式存在:
static JSBool JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
static JSBool JSConstructor(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval);
static void JSDestructor(JSContext *cx, JSObject *obj);
创建另外一个叫JSInit的静态方法,见下面的例子,该方法将在应用程序创建JavaScript runtime时被调用。
static JSObject *JSInit(JSContext *cx, JSObject *obj, JSObject *proto);
JSInit方法的实现大约如下:
JSObject *JSCustomer::JSInit(JSContext *cx, JSObject *obj, JSObject *proto)
{
JSObject *newObj = JS_InitClass(cx, obj, proto, &customerClass,
JSCustomer::JSConstructor, 0,
JSCustomer::customer_properties, JSCustomer::customer_methods,
NULL, NULL);
return newObj;
}
对象在脚本中被具象化(译注:instantiated,简而言之就是对象new出来的时候)的时候,静态方法JSConstructor会被调用。在这个方法中可以用JS_SetPrivate API给该对象附加一些自定义数据。
JSBool JSCustomer::JSConstructor(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
JSCustomer *p = new JSCustomer();
p->setCustomer(new Customer());
if ( ! JS_SetPrivate(cx, obj, p) )
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
return JS_TRUE;
}
JSConstructor构造方法可以带多个参数,用来初始化类。目前为止已经在堆上创建了一个指针,还需要一种途径来销毁它,这可以通过JS_Destructor完成:
void JSCustomer::JSDestructor(JSContext *cx, JSObject *obj)
{
JSCustomer *p = JS_GetPrivate(cx, obj);
delete p;
p = NULL;
}
添加一个JSPropertySpec类型的静态成员数组来存放属性信息,同时定义属性ID的枚举变量。
static JSPropertySpec customer_properties[];
enum
{
name_prop,
age_prop
};
在实现文件中如下初始化该数组:
JSPropertySpec JSCustomer::customer_properties[] =
{
{ "name", name_prop, JSPROP_ENUMERATE },
{ "age", age_prop, JSPROP_ENUMERATE },
{ 0 }
};
数组的最后一个元素必须为空,其中每个元素是一个带有3个元素的数组。第一个元素是给JavaScript用的名字。第二个元素是该属性的唯一ID,将传递给回调函数。第三个元素是标志位,JSPROP_ENUMERATE代表脚本在枚举Customer对象的所有属性时可以看到该属性,也可以指定JSPROP_READONLY来表明该属性不允许被脚本程序改变。
现在可以实现该属性的getting和setting回调函数了:
JSBool JSCustomer::JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (JSVAL_IS_INT(id))
{
Customer *priv = (Customer *) JS_GetPrivate(cx, obj);
switch(JSVAL_TO_INT(id))
{
case name_prop:
break;
case age_prop:
*vp = INT_TO_JSVAL(priv->getCustomer()->GetAge());
break;
}
}
return JS_TRUE;
}
JSBool JSCustomer::JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (JSVAL_IS_INT(id))
{
Customer *priv = (Customer *) JS_GetPrivate(cx, obj);
switch(JSVAL_TO_INT(id))
{
case name_prop:
break;
case age_prop:
priv->getCustomer()->SetAge(JSVAL_TO_INT(*vp));
break;
}
}
return JS_TRUE;
}
建议在属性的回调函数中返回JS_TRUE。如果返回JS_FALSE,则当该属性在对象中没找到时(脚本引擎)不会进行搜索。
创建一个JSFunctionSpec类型的静态成员数组:
static JSFunctionSpec customer_methods[];
在实现文件中如下初始化该数组:
JSFunctionSpec wxJSFrame::wxFrame_methods[] =
{
{ "computeReduction", computeReduction, 1, 0, 0 },
{ 0 }
};
最后一个元素必须为空,其中每个元素是一个带有5个元素的数组。第一个元素是给脚本程序用的方法名称。第二个是一个全局或者静态成员函数的名称。第三个是该方法的参数个数。最后两个可以忽略。
在类中创建一个静态方法:
static JSBool computeReduction(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval);
该函数成功时返回JS_TRUE,否则返回JS_FALSE。注意真正的JavaScript方法的返回值保存在rval参数中。
该方法的一个实现例子:
JSBool JSCustomer::computeReduction(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval)
{
JSCustomer *p = JS_GetPrivate(cx, obj);
if ( p->getCustomer()->GetAge() < 25 )
*rval = INT_TO_JSVAL(10);
else
*rval = INT_TO_JSVAL(5);
return JS_TRUE;
}
下面的脚本使用了前面创建的对象:
var c = new Customer();
c.name = "Franky";
c.age = 32;
var reduction = c.computeReduction();
别忘了在创建context时初始化JavaScript对象:
JSObject *obj = JSCustomer::JSInit(cx, global);
“在C++中怎么执行JavaScript程序”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。