溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在C++中怎么執行JavaScript程序

發布時間:2021-11-20 14:06:21 來源:億速云 閱讀:553 作者:iii 欄目:開發技術

本篇內容介紹了“在C++中怎么執行JavaScript程序”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

SpiderMonkey

SpiderMonkey是Mozilla項目的一部分,用C語言寫成,是負責執行JavaScript腳本的引擎。另外還有一個叫Rhino的Java引擎。

SpiderMonkey的最新版本可在這里下載。它是以源代碼形式發布的,因此你必須自己編譯它。Visual C++用戶可以在src目錄下找到Workspace項目工程文件來編譯,編譯結果會產生一個叫'js32.dll'的dll文件。

SpiderMonkey也可以在Macintosh和Unix上使用。

在C++中執行JavaScript程序

步驟1-創建JavaScript runtime(運行時實例)

初始化一個JavaScript runtime可用JS_NewRuntime方法,該方法將為runtime分配內存,同時還得指定一個字節數,當內存分配超過這個數字時垃圾收集器會自動運行。

JSRuntime *rt = JS_NewRuntime(1000000L);
if ( rt == NULL )
{
    // Do some error reporting
}

步驟2-創建context(上下文環境)

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
}

步驟3-初始化全局對象

在一個腳本開始運行前,必須初始化一些大多數腳本會用到的通用的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);

步驟4-執行腳本

執行腳本的一種途徑是使用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);

步驟5-清理腳本引擎

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

在C++中定義一個在JavaScript中用的類

這個例子中用到的類定義如下:

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類派生一個你想在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);

步驟2-初始化你的JavaScript對象

創建另外一個叫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;
}

步驟3-添加屬性

添加一個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,則當該屬性在對象中沒找到時(腳本引擎)不會進行搜索。

步驟4-添加方法

創建一個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程序”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女