您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“在C++中怎么執(zhí)行JavaScript程序”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
SpiderMonkey是Mozilla項(xiàng)目的一部分,用C語言寫成,是負(fù)責(zé)執(zhí)行JavaScript腳本的引擎。另外還有一個(gè)叫Rhino的Java引擎。
SpiderMonkey的最新版本可在這里下載。它是以源代碼形式發(fā)布的,因此你必須自己編譯它。Visual C++用戶可以在src目錄下找到Workspace項(xiàng)目工程文件來編譯,編譯結(jié)果會(huì)產(chǎn)生一個(gè)叫'js32.dll'的dll文件。
SpiderMonkey也可以在Macintosh和Unix上使用。
初始化一個(gè)JavaScript runtime可用JS_NewRuntime方法,該方法將為runtime分配內(nèi)存,同時(shí)還得指定一個(gè)字節(jié)數(shù),當(dāng)內(nèi)存分配超過這個(gè)數(shù)字時(shí)垃圾收集器會(huì)自動(dòng)運(yùn)行。
JSRuntime *rt = JS_NewRuntime(1000000L);
if ( rt == NULL )
{
// Do some error reporting
}
Context指明了腳本運(yùn)行所需的棧大小,即分配給腳本執(zhí)行棧的私有內(nèi)存數(shù)量。每個(gè)腳本都和它自己的context相關(guān)聯(lián)。
當(dāng)一個(gè)context正在被某個(gè)腳本或線程使用時(shí),其他腳本或線程不能使用該context。不過在腳本或線程結(jié)束時(shí),該context可以被下一個(gè)腳本或線程重用。
創(chuàng)建一個(gè)新context可用JS_NewContext方法。context必須關(guān)聯(lián)到一個(gè)runtime,調(diào)用JS_NewContext方法時(shí)還必須指定棧的大小。
JSContext *cx = JS_NewContext(m_rt, 8192);
if ( cx == NULL )
{
// Do some error reporting
}
在一個(gè)腳本開始運(yùn)行前,必須初始化一些大多數(shù)腳本會(huì)用到的通用的JavaScript函數(shù)和內(nèi)置(build-in)類對(duì)象。
全局對(duì)象是在一個(gè)JSClass結(jié)構(gòu)中描述的。該結(jié)構(gòu)可以按以下方式初始化:
JSClass globalClass =
{
"Global", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub
};
現(xiàn)在創(chuàng)建和初始化這個(gè)全局對(duì)象:JSObject *globalObj = JS_NewObject(cx, &globalClass, 0, 0); JS_InitStandardClasses(cx, globalObj);
執(zhí)行腳本的一種途徑是使用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);
在這個(gè)腳本中,如果運(yùn)行正確的話當(dāng)天數(shù)據(jù)會(huì)保存在rval中。rval包含最后一個(gè)執(zhí)行函數(shù)的結(jié)果。JS_EvaluteScript返回JS_TRUE代表執(zhí)行成功,返回JS_FALSE則代表有錯(cuò)誤發(fā)生。
從rval得到相應(yīng)的字符串值可以用下面的方法。在這里我不想解釋所有細(xì)節(jié),想獲得更詳細(xì)的信息請(qǐng)自己查API文檔。
JSString *str = JS_ValueToString(cx, rval);
std::cout << JS_GetStringBytes(str);
程序結(jié)束前必須對(duì)腳本引擎做一些清理工作:JS_DestroyContext(cx); JS_DestroyRuntime(rt);
這個(gè)例子中用到的類定義如下:
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類派生一個(gè)你想在JavaScript中用的新的C++類,或者創(chuàng)建一個(gè)包含一個(gè)Customer類型成員變量的新類。
給JavaScript用的類得有一個(gè)JSClass結(jié)構(gòu),為此得創(chuàng)建一個(gè)JSClass類型的靜態(tài)成員變量,該變量會(huì)被其他類用到,因此還得把它聲明為public變量。別的類可以用該結(jié)構(gòu)來判斷對(duì)象的類型(見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結(jié)構(gòu)里包含了JavaScript類的名字、標(biāo)志位以及給腳本引擎用的回調(diào)函數(shù)的名字。舉個(gè)例子,腳本引擎使用回調(diào)函數(shù)從類中獲取某個(gè)屬性值。
在C++類的實(shí)現(xiàn)文件中定義JSClass結(jié)構(gòu)如下:
// 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
};
用到的回調(diào)函數(shù)是JSCustomer::JSGetProperty,JSCustomer::JSSetProperty和JSCustomer::JSDestructor。腳本引擎調(diào)用JSGetProperty獲取屬性值,調(diào)用JSSetProperty設(shè)置屬性值,調(diào)用JSDestructor析構(gòu)JavaScript對(duì)象。
JSCLASS_HAS_PRIVATE標(biāo)志位會(huì)讓腳本引擎分配一些內(nèi)存,這樣你可以在JavaScript對(duì)象中附加一些自定義數(shù)據(jù),比如可以用它來保存類指針。
回調(diào)函數(shù)以C++的類靜態(tài)成員函數(shù)方式存在:
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);
創(chuàng)建另外一個(gè)叫JSInit的靜態(tài)方法,見下面的例子,該方法將在應(yīng)用程序創(chuàng)建JavaScript runtime時(shí)被調(diào)用。
static JSObject *JSInit(JSContext *cx, JSObject *obj, JSObject *proto);
JSInit方法的實(shí)現(xiàn)大約如下:
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;
}
對(duì)象在腳本中被具象化(譯注:instantiated,簡(jiǎn)而言之就是對(duì)象new出來的時(shí)候)的時(shí)候,靜態(tài)方法JSConstructor會(huì)被調(diào)用。在這個(gè)方法中可以用JS_SetPrivate API給該對(duì)象附加一些自定義數(shù)據(jù)。
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構(gòu)造方法可以帶多個(gè)參數(shù),用來初始化類。目前為止已經(jīng)在堆上創(chuàng)建了一個(gè)指針,還需要一種途徑來銷毀它,這可以通過JS_Destructor完成:
void JSCustomer::JSDestructor(JSContext *cx, JSObject *obj)
{
JSCustomer *p = JS_GetPrivate(cx, obj);
delete p;
p = NULL;
}
添加一個(gè)JSPropertySpec類型的靜態(tài)成員數(shù)組來存放屬性信息,同時(shí)定義屬性ID的枚舉變量。
static JSPropertySpec customer_properties[];
enum
{
name_prop,
age_prop
};
在實(shí)現(xiàn)文件中如下初始化該數(shù)組:
JSPropertySpec JSCustomer::customer_properties[] =
{
{ "name", name_prop, JSPROP_ENUMERATE },
{ "age", age_prop, JSPROP_ENUMERATE },
{ 0 }
};
數(shù)組的最后一個(gè)元素必須為空,其中每個(gè)元素是一個(gè)帶有3個(gè)元素的數(shù)組。第一個(gè)元素是給JavaScript用的名字。第二個(gè)元素是該屬性的唯一ID,將傳遞給回調(diào)函數(shù)。第三個(gè)元素是標(biāo)志位,JSPROP_ENUMERATE代表腳本在枚舉Customer對(duì)象的所有屬性時(shí)可以看到該屬性,也可以指定JSPROP_READONLY來表明該屬性不允許被腳本程序改變。
現(xiàn)在可以實(shí)現(xiàn)該屬性的getting和setting回調(diào)函數(shù)了:
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;
}
建議在屬性的回調(diào)函數(shù)中返回JS_TRUE。如果返回JS_FALSE,則當(dāng)該屬性在對(duì)象中沒找到時(shí)(腳本引擎)不會(huì)進(jìn)行搜索。
創(chuàng)建一個(gè)JSFunctionSpec類型的靜態(tài)成員數(shù)組:
static JSFunctionSpec customer_methods[];
在實(shí)現(xiàn)文件中如下初始化該數(shù)組:
JSFunctionSpec wxJSFrame::wxFrame_methods[] =
{
{ "computeReduction", computeReduction, 1, 0, 0 },
{ 0 }
};
最后一個(gè)元素必須為空,其中每個(gè)元素是一個(gè)帶有5個(gè)元素的數(shù)組。第一個(gè)元素是給腳本程序用的方法名稱。第二個(gè)是一個(gè)全局或者靜態(tài)成員函數(shù)的名稱。第三個(gè)是該方法的參數(shù)個(gè)數(shù)。最后兩個(gè)可以忽略。
在類中創(chuàng)建一個(gè)靜態(tài)方法:
static JSBool computeReduction(JSContext *cx, JSObject *obj, uintN argc,
jsval *argv, jsval *rval);
該函數(shù)成功時(shí)返回JS_TRUE,否則返回JS_FALSE。注意真正的JavaScript方法的返回值保存在rval參數(shù)中。
該方法的一個(gè)實(shí)現(xiàn)例子:
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;
}
下面的腳本使用了前面創(chuàng)建的對(duì)象:
var c = new Customer();
c.name = "Franky";
c.age = 32;
var reduction = c.computeReduction();
別忘了在創(chuàng)建context時(shí)初始化JavaScript對(duì)象:
JSObject *obj = JSCustomer::JSInit(cx, global);
“在C++中怎么執(zhí)行JavaScript程序”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。