溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

在C++中怎么執(zhí)行JavaScript程序

發(fā)布時(shí)間:2021-11-20 14:06:21 來源:億速云 閱讀:523 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“在C++中怎么執(zhí)行JavaScript程序”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

SpiderMonkey

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上使用。

在C++中執(zhí)行JavaScript程序

步驟1-創(chuàng)建JavaScript runtime(運(yùn)行時(shí)實(shí)例)

初始化一個(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
}

步驟2-創(chuàng)建context(上下文環(huán)境)

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
}

步驟3-初始化全局對(duì)象

在一個(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);

步驟4-執(zhí)行腳本

執(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);

步驟5-清理腳本引擎

程序結(jié)束前必須對(duì)腳本引擎做一些清理工作:JS_DestroyContext(cx);
JS_DestroyRuntime(rt);

在C++中定義一個(gè)在JavaScript中用的類

這個(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;
};

步驟1-JavaScript類

從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);

步驟2-初始化你的JavaScript對(duì)象

創(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;
}

步驟3-添加屬性

添加一個(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)行搜索。

步驟4-添加方法

創(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í)用文章!

向AI問一下細(xì)節(jié)

免責(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)容。

AI