溫馨提示×

溫馨提示×

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

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

怎么進行JavaScript-C/C++引擎嵌入開發(fā)

發(fā)布時間:2021-10-27 16:29:36 來源:億速云 閱讀:137 作者:柒染 欄目:開發(fā)技術(shù)

怎么進行JavaScript-C/C++引擎嵌入開發(fā),相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

JavaScript-C/C++引擎概覽

本文檔提供了一個JavaScript(JS)引擎的C語言實現(xiàn)的概述,他介紹了你如何在你的應(yīng)用程序中嵌入腳本引擎來讓它們可以使用JS。有兩大理由讓你在應(yīng)用程序中嵌入JS引擎:使用腳本來自動操作你的應(yīng)用程序;同時使用JS引擎和腳本無論何時都可以提供跨平臺的功能并消除了應(yīng)用程序解決方案對平臺的依賴性。

受支持的JavaScript版本

本JS引擎支持從JS 1.0版到JS1.4。JS 1.3和更高版本符合ECMAScript-262規(guī)范。JS引擎解析、編譯和執(zhí)行包含JS語句和函數(shù)的腳本。這個引擎可以處理要用來執(zhí)行腳本的JS數(shù)據(jù)類型和對象內(nèi)存分配,同時它可以清除——垃圾回收——內(nèi)存中已經(jīng)不需要的數(shù)據(jù)類型和對象。

你如何使用這個引擎?

通常,你將JS引擎作為一個共享的資源進行構(gòu)建。例如,在Windows和Windows NT上,這個引擎是一個DLL文件,在Unix上是一個共享庫。然后你把你的應(yīng)用程序和他連接,同時嵌入式JS引擎應(yīng)用程序編程接口(API)就可以在你的應(yīng)用程序中調(diào)用了。JS引擎的API提供了以下幾種分類的函數(shù):

  • 數(shù)據(jù)類型操作

  • 運行時控制

  • 類和對象創(chuàng)生的維護

  • 函數(shù)和腳本執(zhí)行

  • 字符串處理

  • 錯誤處理

  • 安全性控制

  • 調(diào)試支持

你在每個嵌入了JS調(diào)用的應(yīng)用程序中將會用到這些功能分類中的某些部分,象運行時控制和數(shù)據(jù)類型操作。例如,在你調(diào)用其他JS功能之前,你必須通過調(diào)用JS_NewRuntime函數(shù)來新建和初始化JS引擎。其他功能分類,像安全控制,提供一些可選的特性,你可以根據(jù)需要在你的應(yīng)用程序中使用它們。

這個引擎和應(yīng)用程序有什么關(guān)系?

從概念上來講,JS引擎在你的系統(tǒng)上是一個共享資源。通過在你的應(yīng)用程序中嵌入引擎API命令你可以向JS引擎?zhèn)鬟f處理的請求。這個引擎,反過來,處理你的請求,并把返回值或者狀態(tài)信息返回給你的應(yīng)用程序。圖1.1描述了它們一般的關(guān)系:

圖 1.1

怎么進行JavaScript-C/C++引擎嵌入開發(fā)

例如,假設(shè)你正在使用JS引擎來使你的應(yīng)用程序能通過JS腳本自動運行,同時假設(shè)你的應(yīng)用程序運行一個腳本來對一個用戶進行身份驗證并且設(shè)置一個用戶對這個應(yīng)用程序的訪問權(quán)限。首先,你的應(yīng)用程序可能新建一個代表用戶的自定義JS對象,包括了用戶的名字、ID、訪問權(quán)限和一個可能的用戶擁有權(quán)限在應(yīng)用程序中使用的函數(shù)的列表。

在這個情況下,你的應(yīng)用程序給JS引擎發(fā)送的的第一個請求可能是對JS_NewObject的調(diào)用來新建一個自定義對象。當(dāng)JS引擎新建了這個對象,他返回一個指針給你的應(yīng)用程序。你的應(yīng)用程序可以再次調(diào)用JS引擎來執(zhí)行使用這個對象的腳本。例如,在建立了用戶對象之后,你的應(yīng)用程序會立刻給JS_EvaluateScript傳遞一個腳本來立刻編譯執(zhí)行。那個腳本可以獲得并驗證用戶信息,然后建立用戶對其他應(yīng)用程序特性的訪問權(quán)限。

事實上,你的應(yīng)用程序和JS引擎之間的關(guān)系遠(yuǎn)比圖1.1中顯示的要復(fù)雜的多。例如,它假設(shè)你已經(jīng)為你的平臺構(gòu)建了JS引擎。它還假設(shè)你的應(yīng)用程序包含了jsapi.h還假設(shè)應(yīng)用程序?qū)σ孢M行的第一個調(diào)用已經(jīng)初始化了JS運行時。

當(dāng)JS引擎接受到了一個初始化的請求時,他會為JS運行時分配內(nèi)存。圖1.2描述了這個過程:

圖 1.2

怎么進行JavaScript-C/C++引擎嵌入開發(fā)

這個運行時是一個內(nèi)存空間,在其中可以維護你的應(yīng)用程序所使用的變量、對象和上下文。一個上下文是指,針對JS引擎所使用的線程的腳本執(zhí)行狀態(tài)。每個同時存在的腳本或者線程都必須有它自己的上下文。一個單獨的JS運行時可以包含很多上下文、對象和變量。

幾乎所有的JS引擎調(diào)用都要求有一個上下文的參數(shù),所以在創(chuàng)建了運行時之后你的應(yīng)用程序首先要做的一件事情是調(diào)用JS_NewContext來至少創(chuàng)建一個上下文。實際你需要的上下文數(shù)量由你的應(yīng)用程序中所期望同時運行的腳本的數(shù)量決定。從另一方面說,如果同一時間只有一個腳本被編譯執(zhí)行,那么你就知需要建立單獨的一個上下文,你可以對每個腳本重復(fù)使用它。

在你新建了上下文之后,你會通常想要初始化引擎內(nèi)置的JS對象,可以通過調(diào)用JS_InitStandardClasses實現(xiàn)。內(nèi)置的對象有Array,Boolean,Date,Math,Number,和String字符串對象,大多數(shù)腳本都會用到。

大多數(shù)應(yīng)用程序也要用到自定義JS對象。這些對象是特定于你的應(yīng)用程序的。他們通常代表了數(shù)據(jù)結(jié)構(gòu)和應(yīng)用程序中腳本使用的方法。要新建一個自定義對象,你要組裝一個JS類來生成這個對象,調(diào)用JS_InitClass來在運行時設(shè)立這個類,然后調(diào)用JS_NewObject來在引擎中新建你這個自定義對象的實例。最后,如果你的對象有一些屬性,你也許要通過調(diào)用JS_SetProperty來設(shè)置他們的默認(rèn)值。

即使你在創(chuàng)建一個對象的時候給JS引擎?zhèn)鬟f了一個特定的上下文,最后這個對象還是獨立于這個上下文存在的。任何腳本都可以和任意上下文相關(guān)聯(lián)來訪問任何對象。圖1.3描述了腳本和運行時、上下文以及對象之間的關(guān)系。

圖 1.3

怎么進行JavaScript-C/C++引擎嵌入開發(fā)

如圖1.3所示,腳本和上下文完全是互相獨立存在的及時他們可以訪問相同的對象。在給定的運行時中,一個應(yīng)用程序可以任意未分配的上下文來訪問任何對象。也可能有時你想確保能為獨占的使用而保留某些上下文和對象。在這些情況下,給你的應(yīng)用程序新建單獨的運行時:一個針對共享上下文和對象,另一個(或者更多的,取決于你的應(yīng)用程序的需求)針對私有的運行時和對象。

注意:同一時間只能有一個線程被授權(quán)訪問特定上下文。

構(gòu)建引擎

在你可以在你的應(yīng)用程序中使用JS之前,你必須將JS引擎構(gòu)建成一個可共享的庫。在大多數(shù)情況下,引擎代碼已經(jīng)包括了Make文件來自動構(gòu)建。

例如,在Unix下,js源代碼目錄包括了一個基本的Gnu Make文件——Makefile.ref和一個config目錄。config目錄包括了平臺特定的.mk文件來配合Makefile.ref對你的環(huán)境進行構(gòu)建。在Windows
NT下,NMake文件是js.mak

請閱讀源代碼目錄中任何的readme文件,也許其中包括了和更新的編譯指導(dǎo)或者其他信息。

嵌入引擎有什么必要條件?

如果要讓你的應(yīng)用程序可以執(zhí)行JS,就要在你的應(yīng)用程序代碼中嵌入合適的引擎。嵌入一般有五步:

  1. 在你的C模塊中加入#include jsapi.h來確保編譯器知道有哪些引擎的API可以調(diào)用。極少部分特殊的JS引擎工作時會要求你包含額外的頭文件。例如,要在你的應(yīng)用程序中調(diào)用JS調(diào)試器,你要在合適的模塊里面包含jsdbgapi.h。

    大部分在JS源代碼中的其它的頭文件不應(yīng)該被引用。這樣做可能會使你的程序依賴于引擎內(nèi)部的接口,而這些接口可能隨著版本發(fā)布而更改。

  1. 在你的應(yīng)用程序中提供支持結(jié)構(gòu)和變量聲明。例如,如果你打算給JS引擎?zhèn)鬟f一個腳本呢,提供一個字符串變量保存了你的應(yīng)用程序的腳本的版本的文字信息。使用jsapi.h中定義的JS數(shù)據(jù)類型來聲明結(jié)構(gòu)和變量。

  2. 使用JavaScript編寫特定應(yīng)用的對象。這些對象常常會與操作在你C程序中的結(jié)構(gòu)的結(jié)構(gòu)和方法進行通訊,特別是如果你在使用JS引擎來自動操作你的應(yīng)用程序。

  3. 在程序代碼中嵌入合適的JS引擎API調(diào)用和變量引用,包括初始化內(nèi)置JS對象,和創(chuàng)建組成任何應(yīng)用程序要用的自定義對象。

  4. 大多數(shù)JS引擎調(diào)用都會返回一個值。如果這個值是零或者空,它通常表示一個錯誤的情況發(fā)生了。如果值非零,它一般表示成功;在這些情況下,返回的值常常會是你的程序需要使用的指針,或者存起來留以后引用。很重要的是,你的程序至少應(yīng)該每次檢查JS調(diào)用返回的值。

以下代碼片斷描述了嵌入使用的大部分過程,除了JS腳本的建立,這點也不在本文的介紹范圍之內(nèi)。如要查詢有關(guān)創(chuàng)建腳本的信息——JavaScript這個語言——請看客戶端JavaScript指導(dǎo),如果要得到關(guān)于編寫服務(wù)器端對象,見服務(wù)器端JavaScript指導(dǎo)。

.
.
.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 包含JS引擎API頭文件 */
#include "jsapi.h"
.
.
.

/* 主程序建立全局JS變量,包括運行時,
* 一個上下文,和一個全局對象,然后初始化JS運行時,
* 并創(chuàng)建一個上下文. */

int main(int argc, char **argv)
{
int c, i;
/*建立全局的JS變量,包括全局和自定義對象 */

JSVersion version;
JSRuntime *rt;
JSContext *cx;
JSObject *glob, *it;
JSBool builtins;

/* 初始化JS運行時,并返回結(jié)果給rt */
rt = JS_NewRuntime(8L * 1024L * 1024L);

/* 如果rt沒有值,結(jié)束程序 */
if (!rt)
return 1;

/* 新建一個上下文并且把它和JS運行時相關(guān)聯(lián) */
cx = JS_NewContext(rt, 8192);

/* 如果cx沒有值,在此結(jié)束程序 */
if (cx == NULL)
return 1;

/* 新建全局對象 */
glob = JS_NewObject(cx, clasp, NULL, NULL);

/* 初始化內(nèi)置JS對象和全局對象 */
builtins = JS_InitStandardClasses(cx, glob);

.
.
.

return 0;

}

這個范例代碼十分簡單,它描述了嵌入JS引擎調(diào)用所必須的關(guān)鍵元素。如果想要更完整的例子——也就是以上這段代碼片斷的出處——參見js.c,這個范例應(yīng)用的源代碼是包含在JS引擎的源代碼中的。

理解關(guān)鍵嵌入開發(fā)概念

大多數(shù)你要創(chuàng)建的JavaScript應(yīng)用,你都會想要遵循一些權(quán)威的的JS API嵌入實踐。以下部分講述了任何程序中都要使用到的API調(diào)用。

在很多情況下,嵌入某些特定API調(diào)用的順序決定這個程序的成功。例如,你必須在調(diào)用其它JS引擎函數(shù)之前初始化一個JS運行時。相應(yīng)的,你要在關(guān)閉程序之前釋放JS運行時。因此,典型的程序的main函數(shù)像一種三明治,在任何你提供的功能前后先初始化最后釋放JS運行時:

int main(int argc, char **argv)
{
int c, i;

/*建立全局JS變量,包括全局對象global和自定義對象 */
JSVersion version;
JSRuntime *rt;
JSContext *cx;
JSObject *glob, *it;

.
.
.

/* 初始化JS運行時,并把返回的結(jié)果放在rt中 */
rt = JS_NewRuntime(8L * 1024L * 1024L);

/* 如果rt沒有值,程序結(jié)束。 */
if (!rt)
return 1;

.
.
.

/* 建立一個上下文 */
cx = JS_NewContext(rt, 8192);

/* 如果cx值為空,則結(jié)束程序 */
if (cx == NULL)
return 1;

/* 初始化內(nèi)置JS對象和全局對象 */
builtins = JS_InitStandardClasses(cx, glob);

.
.
.

/* 把你的代碼扔這里,包括你要用來創(chuàng)建自定義JS對象的JS API函數(shù)調(diào)用。
* JS對象模型在這里開始。 */

.
.
.

/* 在退出應(yīng)用程序之前,釋放JS運行時 */
JS_DestroyRuntime(rt);

正如這個例子所描述的,嵌入了JS引擎的函數(shù)的應(yīng)用程序要負(fù)責(zé)建立JS運行時,這是他最先要完成的動作之一,同時它還要負(fù)責(zé)在退出之前釋放運行時。一般來說,確保運行時被初始化和釋放的最佳位置是在中央JS調(diào)度程序的模塊中嵌入必要的調(diào)用,無論你將使用哪一個模塊作為在應(yīng)用程序的中央調(diào)度模塊。

在你初始化了運行時之后,你可以建立應(yīng)用程序的JS對象模型。這個對象模型決定了你的JS對象互相之間的關(guān)系。JS對象本質(zhì)上是分層次的。所有的JS對象都是默認(rèn)與全局(global)對象相關(guān)的,他們都是全局對象的子孫。當(dāng)你初始化標(biāo)準(zhǔn)JS類的時候,你會自動獲得一個全局對象:

builtins = JS_InitStandardClasses(cx, glob);

全局對象會建立一些基本屬性和方法,其他對象都會繼承這些屬性和方法。當(dāng)你創(chuàng)建你自己的對象時,他們可以自動使用這些已經(jīng)定義在全局對象上的屬性和方法。你可以通過在自定義對象上重新對他們進行定義來覆蓋這些默認(rèn)屬性和方法,否則可以直接接受默認(rèn)的賦值。

你也可以基于其他的內(nèi)置JS對象新建自定義對象,或者基于其他自定義對象來新建對象。無論哪種情況,你新建的對象在繼承鏈中將繼承他祖先的所有屬性和方法,一直追溯到全局對象。如果要了解更多關(guān)于全局和自定義對象地內(nèi)容,請參見“初始化內(nèi)置和全局JS對象”以及“創(chuàng)建和初始化自定義對象”。

管理一個運行時

JS運行時是一塊內(nèi)存空間,在這里面JS引擎可以管理與JS函數(shù)和腳本相關(guān)的上下文、對象和變量。在執(zhí)行任何JS函數(shù)或者是腳本之前,你必須初始化一個運行時。初始化運行時的API調(diào)用是JS_NewRuntimeJS_NewRuntime有一個參數(shù),是一個無符號整數(shù),它指明了在垃圾收集發(fā)生之前,分配給運行時的內(nèi)存最大數(shù)值,單位是字節(jié)。例如:

rt = JS_NewRuntime(8L * 1024L * 1024L);

如上面列舉的,JS_NewRuntime也會返回一個值,這個值是一個指向新建的運行時的指針。一個非空的返回值表示運行時被成功創(chuàng)建了。

一般來說,一個應(yīng)用程序只需要一個運行時。但是,你還是可以創(chuàng)建多個運行時的,我們可以在必要的時候調(diào)用JS_NewRuntime并把返回值存在不同的指針中。

當(dāng)不再需要JS運行時的時候,應(yīng)該把它銷毀來釋放他占用的內(nèi)存資源,以便給其他應(yīng)用程序來使用。根據(jù)你的應(yīng)用程序中JS的使用范圍,你可以選擇在JS使用結(jié)束立刻銷毀運行時,或者,你可以選擇一直保留運行時知道你的應(yīng)用程序即將結(jié)束。無論哪種情況,都必須使用JS_DestroyRuntime來釋放運行時,當(dāng)運行時不再需要的時候:

JS_DestroyRuntime(rt);

如果你使用了多個運行時,要確保在結(jié)束應(yīng)用程序前,每一個都被正確釋放了。

管理上下文

幾乎所有的JS API調(diào)用都要求你傳送一個上下文作為參數(shù)。在JavaScript引擎中,一個上下文唯一對應(yīng)一個腳本。引擎把上下文信息傳送給運行腳本的那個線程。每個同步執(zhí)行的腳本必須被分配一個唯一的上下文。當(dāng)一個腳本執(zhí)行完之后,他的上下文就不再被使用了,這時候這個上下文就可以再次被分配給一個新的腳本,或者可以釋放他。

要為一個腳本創(chuàng)建新的上下文,可以使用JS_NewContext函數(shù)。該函數(shù)有兩個參數(shù):一個指針指向上下文所需結(jié)合的運行時,和為上下文分配的??臻g的大小,以字節(jié)為單位。如果成功,函數(shù)返回新建的上下文的指針。例如:

JSContext *cx;
.
.
.
cx = JS_NewContext(rt, 8192);

運行時必須已經(jīng)存在。你為上下文指定的棧的大小應(yīng)該足夠容納使用這個上下文的腳本所創(chuàng)建的任何變量和對象。注意和分配和維護上下文相關(guān)有一個特定的數(shù)量,因為你可能要:

  1. 只創(chuàng)建同一時刻在你的應(yīng)用程序中所需要的數(shù)量一樣多的上下文。

  2. 只要上下文有可能被應(yīng)用程序用到,就保留他們,而不是每當(dāng)需要的時候再重新新建不需要了就立刻銷毀。

當(dāng)不再需要某一個上下文時,應(yīng)該把它銷毀來釋放它占用的內(nèi)存資源留給其他的應(yīng)用使用。根據(jù)你的應(yīng)用程序中的JS使用范圍,你可以選擇在使用完上下文之后,就立刻銷毀,或者,更多情況下,你可以考慮為以后重復(fù)使用來保留上下文直到應(yīng)用程序結(jié)束為止。不管哪種情況,當(dāng)他不再需要用到的時候,可以使用JS_DestroyContext來釋放上下文。這個函數(shù)帶一個參數(shù),也就是指向要銷毀的上下文的指針:

JS_DestroyContext(cx);

如果你的應(yīng)用創(chuàng)建了多個運行時的話,應(yīng)用程序需要了解上下文和哪個運行時相關(guān)聯(lián)。在這種情況下,可以調(diào)用JS_GetRuntime,并且把上下文作為參數(shù)傳遞給他。JS_GetRuntime會返回一個指向某個合適的運行時的指針,如果存在的話:

rt = JS_GetRuntime(cx);

當(dāng)你創(chuàng)建一個上下文的時候,你要給他分配??臻g,這個空間將為那些被使用這個上下文的腳本所創(chuàng)建的變量和對象所使用。你也可以用給定的上下文僅僅用來儲存大量數(shù)據(jù),只要分配所需的最小的??臻g。調(diào)用JS_SetContextPrivate函數(shù)來建立一個指向上下文使用的私有數(shù)據(jù)的指針,并調(diào)用JS_GetContextPrivate函數(shù)來獲取這個指針,這樣就可以訪問這些數(shù)據(jù)了。你的應(yīng)用程序要負(fù)責(zé)創(chuàng)建和管理這個可選的私有數(shù)據(jù)。

若要創(chuàng)建私有數(shù)據(jù)并把它和一個上下文關(guān)聯(lián):

  1. 根據(jù)需要建立私有數(shù)據(jù),可以使用一個普通的 C void 指針變量。

  2. 調(diào)用JS_SetContextPrivate,并指明通過哪個上下文來建立私有數(shù)據(jù),并給出指向數(shù)據(jù)的指針。

例如:

JS_SetContextPrivate(cx, pdata);

如果要在以后獲取數(shù)據(jù),調(diào)用JS_GetContextPrivate函數(shù),并把上下文作為參數(shù)傳遞給他。該函數(shù)會返回指向私有數(shù)據(jù)的指針:

pdata = JS_GetContextPrivate(cx);

初始化內(nèi)置的和全局的JS對象

JavaScript引擎提供了一些內(nèi)置對象,他們會簡化你的某些開發(fā)任務(wù)。例如,內(nèi)置的Array對象讓你更方便地在JS引擎中創(chuàng)建和處理數(shù)組結(jié)構(gòu)。類似,Date對象提供了一個統(tǒng)一的處理日期數(shù)據(jù)的機制。要查閱引擎支持的內(nèi)置對象的完整列表,請參看JS_InitStandardClasses。

JS引擎始終使用函數(shù)和全局對象。一般來說,全局對象存在于JS腳本的場景背后,為所有其它JS對象提供了一個默認(rèn)的空間范圍和存儲了你在程序中創(chuàng)建和使用的全局變量。在你創(chuàng)建你自己的對象之前,你需要初始化全局對象。函數(shù)對象將啟用對象支持和構(gòu)造器調(diào)用。

JS_InitStandardClasses, 這個API調(diào)用將初始化全局和函數(shù)對象還有引擎內(nèi)置的對象,這樣你的應(yīng)用程序就可以使用他們了:

JSBool builtins;
.
.
.
builtins = JS_InitStandardClasses(cx, glob);

JS_InitStandardClasses會返回一個JS boolean值來表示初始化是否成功。

你可以為你的應(yīng)用程序指定一個不同的全局對象。例如,Netscape Navigator就使用了他自己的全局對象window。若要為你的應(yīng)用程序更改全局對象,可以調(diào)用JS_SetGlobalObject函數(shù)。詳細(xì)信息請查閱JS_SetGlobalObject的參考條目。

創(chuàng)建和初始化自定義對象

除了可以使用引擎內(nèi)置對象之外,你還可以新建、初始化和使用你自己的JS對象。如果你使用JS引擎處理腳本對你的應(yīng)用進行自動化操作,這點尤其重要。自定義JS對象可以提供最直接的程序服務(wù),另外他們也可以作為你的程序服務(wù)的一個接口。例如,一個自定義JS對象提供了某種直接的服務(wù),像處理應(yīng)用程序所有的網(wǎng)絡(luò)訪問、作為數(shù)據(jù)服務(wù)的中間層。也可以是使用一個JS對象映射到應(yīng)用程序中以后的數(shù)據(jù)和函數(shù)中,這樣能為C代碼提供一個面向?qū)ο蟮慕涌凇_@樣一個自定義對象對應(yīng)用程序自身來說扮演了一個接口的角色——從應(yīng)用程序中把值傳遞給用戶,并且接受和處理用戶的輸入然后再返回給應(yīng)用程序。這種對象也可以用來對應(yīng)用程序內(nèi)部的函數(shù)進行訪問控制。

有兩種方法可以創(chuàng)建自定義對象:

  • 寫一個JS腳本,用來創(chuàng)建對象,以及他的屬性、方法和構(gòu)造器,然后把這個腳本在運行時傳遞給JS引擎。

  • 在你的程序中嵌入定義對象的屬性和方法的代碼,調(diào)用引擎來初始化新對象,然后通過其它的引擎調(diào)用來設(shè)置對象的屬性。這個方法的一個好處是你的程序可以包含直接處理所嵌對象的本地方法。

無論哪種情況,如果你創(chuàng)建了一個對象然后要將他在運行時中持久化,以便在此運行時中可以被其他對象調(diào)用,那么你必須通過JS_AddRoot JS_AddNamedRoot調(diào)用來確定這個對象的“根”。使用這兩個函數(shù)會確保JS引擎去跟蹤這些對象并在適當(dāng)?shù)臅r候通過垃圾收集過程中清理掉他們。

從腳本中建立一個對象

要從腳本中創(chuàng)建自定義JS對象的一個原因是,只需要一個在腳本運行期間存在對象。要創(chuàng)建這種持續(xù)在腳本調(diào)用期間的對象的話,你也可以直接在你的應(yīng)用程序中嵌入對象的代碼,而不用使用一個腳本。

注意:你同樣可以使用腳本創(chuàng)建持久對象。

要使用腳本創(chuàng)建一個自定義對象:

  1. 定義和說明對象。他的目的是什么?他的數(shù)據(jù)成員(屬性)有哪些?他有哪些方法(函數(shù))?他是否需要一個運行時構(gòu)造函數(shù)?

  2. 編寫出定義和創(chuàng)建對象的JS腳本。例如:

    function myfun(){
    var x = newObject();
    .
    .
    .
    }

    注意:使用JavaScript編寫的對象并不在應(yīng)用程序嵌入JS引擎的代碼中。關(guān)于對象編寫的更多內(nèi)容,請參閱《客戶端JavaScript指導(dǎo)》和《服務(wù)器端JavaScript指導(dǎo)》。

    在應(yīng)用程序中嵌入合適的JS引擎調(diào)用來編譯和執(zhí)行腳本。你有兩種選擇:1.) 僅使用一個函數(shù)調(diào)用來編譯和執(zhí)行腳本:JS_EvaluateScript,JS_EvaluateUCScript或者2.)
    使用JS_CompileScript或者JS_CompileUCScript,來一次性編譯腳本,然后可以用一個獨立的函數(shù)調(diào)用JS_ExecuteScript.
    來重復(fù)執(zhí)行已經(jīng)編譯的代碼。這些調(diào)用的“UC”版可以提供對統(tǒng)一碼腳本的支持。

你使用腳本創(chuàng)建的一個對象只可以在腳本的生命周期內(nèi)啟用,或者也可以在腳本運行結(jié)束之后持久化。一般來說,一旦腳本運行結(jié)束,他的所有對象都會被銷毀。在大部分情況下,這種行為正是你的應(yīng)用程序需要的。然而,在其他的情況下,你可能希望某對象持續(xù)在腳本之間,或者你的應(yīng)用程序的整個生命周期。這樣的話你需要直接在你的應(yīng)用程序中嵌入對象創(chuàng)建代碼,或者你必須把對象直接連接到全局對象這樣他會一直持續(xù)只要全局對象本身存在。

在應(yīng)用程序中嵌入一個自定義對象

當(dāng)必須進行對象持久化時,或者你認(rèn)為需要對幾個腳本都可用的對象時,嵌入一個自定義JS對象在應(yīng)用程序中是很有用的。例如,一個代表了用戶的ID和訪問權(quán)限的自定義對象可能會在應(yīng)用程序的整個生命期中都會用到。他事先一次性創(chuàng)建和組裝了對象,節(jié)省了很多時間,而不用每次要檢驗用戶ID或者權(quán)限時一遍又一遍用腳本創(chuàng)建對象。

一種在應(yīng)用程序中嵌入自定義對象的方法是:

  1. 創(chuàng)建一個JSPropertySpec數(shù)據(jù)類型,并把它和屬性的信息組裝成對象的屬性,包括參數(shù)的獲?。╣et)和設(shè)置(set)方法的名稱。

  2. 創(chuàng)建一個JSFunctionSpec數(shù)據(jù)類型,并把它和方法的信息組裝成對象使用的方法。

  3. 創(chuàng)建一個實際的C函數(shù)用來處理對象的方法調(diào)用。

  4. 調(diào)用JS_NewObject或者JS_ConstructObject來實例化這個對象。

  5. 調(diào)用JS_DefineFunctions來創(chuàng)建這個對象的方法。

  6. 調(diào)用JS_DefineProperties來創(chuàng)建這個對象的屬性。

描述持久的自定義JS對象的代碼必須放在應(yīng)用程序執(zhí)行的開始部分附近,在任何依賴于該對象的代碼之前。嵌入的實例化和組裝自定義對象的引擎調(diào)用也應(yīng)該出現(xiàn)在任何依賴這個對象的代碼之前。

注意:在大多數(shù)情況下還有一個更方便的在程序代碼中創(chuàng)建自定義對象的方法是調(diào)用JS_DefineObject來創(chuàng)建對象,然后反復(fù)調(diào)用JS_SetProperty來設(shè)置對象的屬性。關(guān)于定義一個對象的更多的信息,參見JS_DefineObject。關(guān)于設(shè)置對象屬性的更多信息,參見JS_SetProperty

為對象提供私有數(shù)據(jù)

像上下文那樣,你可以把大量的數(shù)據(jù)和一個對象相關(guān)聯(lián)而無需把數(shù)據(jù)存儲在這個對象中。調(diào)用JS_SetPrivate來建立一個指向私有數(shù)據(jù)的指針,并且調(diào)用JS_GetPrivate來獲得這個指針這樣就可以訪問數(shù)據(jù)了。你的應(yīng)用程序要對這些可選的私有數(shù)據(jù)的創(chuàng)建和管理負(fù)責(zé)。

要創(chuàng)建私有數(shù)據(jù)并把它和一個對象相關(guān)聯(lián)的話:

  1. 根據(jù)需要建立私有數(shù)據(jù),可以使用一個普通的 C void 指針變量。

  2. 調(diào)用JS_SetPrivate, 制定要為那個對象建立私有數(shù)據(jù),并給出指向數(shù)據(jù)的指針。

例如:

JS_SetPrivate(cx, obj, pdata);

如果要以后再獲取數(shù)據(jù),調(diào)用JS_GetPrivate并且把對象作為一個參數(shù)傳遞。這個函數(shù)將返回一個指向?qū)ο笏接袛?shù)據(jù)的指針:

pdata =JS_GetPrivate(cx, obj);

處理統(tǒng)一碼(Unicode)

JS引擎現(xiàn)在提供了很多API函數(shù)的支持統(tǒng)一碼的版本。這些函數(shù)允許你直接給引擎?zhèn)鬟f使用統(tǒng)一碼編碼的腳本進行編譯和運行。下面的表格列出了標(biāo)準(zhǔn)引擎函數(shù)和他們對應(yīng)的統(tǒng)一碼版本:

標(biāo)準(zhǔn)函數(shù)

統(tǒng)一碼支持函數(shù)

JS_DefineProperty

JS_DefineUCProperty

JS_DefinePropertyWithTinyId

JS_DefineUCPropertyWithTinyId

JS_LookupProperty

JS_LookupUCProperty

JS_GetProperty

JS_GetUCProperty

JS_SetProperty

JS_SetUCProperty

JS_DeleteProperty2

JS_DeleteUCProperty2

JS_CompileScript

JS_CompileUCScript

JS_CompileScriptForPrincipals

JS_CompileUCScriptForPrincipals

JS_CompileFunction

JS_CompileUCFunction

JS_CompileFunctionForPrincipals

JS_CompileUCFunctionForPrincipals

JS_EvaluateScript

JS_EvaluateUCScript

JS_EvaluateScriptForPrincipals

JS_EvaluateUCScriptForPrincipals

JS_NewString

JS_NewUCString

JS_NewStringCopyN

JS_NewUCStringCopyN

JS_NewStringCopyZ

JS_NewUCStringCopyZ

JS_InternString

JS_InternUCString

JS_InternUCStringN

處理統(tǒng)一碼的函數(shù)工作方式與原來的同名函數(shù)一樣,除了原來的函數(shù)使用參數(shù)char *,而統(tǒng)一碼版本的函數(shù)參數(shù)為jschar
*
。

操作JS數(shù)據(jù)類型

JavaScript定義了他自己的數(shù)據(jù)類型。其中一部分直接對應(yīng)C中的副本。其他的,諸如JSObject,jsdouble,
 JSString,對
JavaScript有特殊意義。

一般而言,你在應(yīng)用程序中聲明和使用JS數(shù)據(jù)類型就和使用標(biāo)準(zhǔn)C數(shù)據(jù)類型一樣。然而,JS引擎對JS數(shù)據(jù)類型,也就是需要超過一個字空間的變量變量JSObject,jsdouble,
JSString有不同的跟蹤。引擎周期性地檢查這些變量,察看他們是否還在使用中。如果不再使用了,就收集他們,釋放存儲空間來重新使用。

垃圾收集可以有效重復(fù)利用堆的資源,但是過分頻繁的垃圾收集也會對性能造成影響。你可以根據(jù)JS運行時控制垃圾收集的頻率,根據(jù)你給程序分配的JS運行時的大小和你應(yīng)用程序使用的JS變量和對象的數(shù)量之間的關(guān)系。如果你的程序要創(chuàng)建和使用很多JS對象和變量,你可能就要分配足夠大的運行時來減少垃圾收集的可能頻率。

注意你的應(yīng)用程序要在任何時候調(diào)用同樣能JS_GC或者JS_MaybeGC來強制進行垃圾收集。JS_GC將強制進行垃圾收集。JS_MaybeGC則會根據(jù)條件進行垃圾收集,如果你調(diào)用這個函數(shù)時,初始化時分配的空間的特定比例已經(jīng)被使用的話,就進行垃圾收集。

操作JS值

除了JS數(shù)據(jù)類型之外,JS引擎也使用JS值,稱之為jsval。一個jsval本質(zhì)上是一個指向任意JS數(shù)據(jù)類型(除了整型)的一個指針。對于整型,jsval直接包含了他自身的整數(shù)值。在其他的情況下,指針還會被編碼,添加關(guān)于它所指的數(shù)據(jù)的類型的額外信息。使用可以提高引擎的效率,同時也可以讓很多API函數(shù)來處理不同類型的數(shù)據(jù)。

引擎API包含了一組用來測試JS值中的JS數(shù)據(jù)類型的宏。有:

  • JSVAL_IS_OBJECT

  • JSVAL_IS_NUMBER

  • JSVAL_IS_INT

  • JSVAL_IS_DOUBLE

  • JSVAL_IS_STRING

  • JSVAL_IS_BOOLEAN

Besides testing ajsval,你也可以檢測他是否屬于一個基本JS數(shù)據(jù)類型 (JSVAL_IS_PRIMITIVE)?;绢愋桶ㄎ炊x(undefined)、空(null)、
布爾(boolean)、數(shù)值(numeric)和字符串(string)類型。

你可以測試jsval所指的值是否為NULL(JSVAL_IS_NULL)
或者void(JSVAL_IS_VOID)。

如果jsval指向了一個JS數(shù)據(jù)類型是JSObject,jsdouble,
或者jsstr,你可以將jsval轉(zhuǎn)換成他的內(nèi)在的類型,只要相應(yīng)使用JSVAL_TO_OBJECT,JSVAL_TO_DOUBLEJSVAL_TO_STRING。在某些情況下,你的應(yīng)用程序或者JS引擎調(diào)用要求使用一個特定的數(shù)據(jù)類型的變量或者參數(shù)而非一個jsval時,就很有用了。類似地,你可以使用OBJECT_TO_JSVAL,DOUBLE_TO_JSVAL,
STRING_TO_JSVAL, 把JSObject,jsdouble,
jsstr相應(yīng)地轉(zhuǎn)換成jsval。

操作JS字符串

在JavaScript中你的很多工作都回涉及字符串。JS引擎實現(xiàn)了一個JS字符串類型,JSString,一個指向JS字符—jschar—數(shù)組的指針,用來處理支持統(tǒng)一碼的字符串。引擎也實現(xiàn)了一系列豐富的通用和統(tǒng)一碼字符串管理程序。最后,JS引擎提供了對限定字符串的支持,這可以將兩個或多個相同的字符串創(chuàng)建時在內(nèi)存中共享一個單獨的實例。對于JSString類型的字符串,引擎會跟蹤和管理字符串資源。

通常情況下,當(dāng)你在處理JS引擎使用的字符串時,你應(yīng)該使用JS API中的字符串處理函數(shù)來創(chuàng)建和復(fù)制字符串。還有創(chuàng)建以空字符結(jié)尾的和特定長度的字符串的例程,以及獲取字符串長度和比較字符串。

統(tǒng)一碼字符串支持

使用統(tǒng)一碼(Unicode)的API字符串函數(shù)的名稱和標(biāo)準(zhǔn)的引擎API字符串行數(shù)是一一對應(yīng)的,規(guī)則如下:如果標(biāo)準(zhǔn)函數(shù)名是JS_NewStringCopyN,相應(yīng)的統(tǒng)一碼版本就是JS_NewUCStringCopyN。同樣有針對限定字符串的支持統(tǒng)一碼的API字符串函數(shù)。

限定字符串支持

為了節(jié)省存儲空間,JS引擎提供了對共享一個單獨的字符串實例支持,這些字符串屬于一些獨立的不可變化的文字。這種被共享的字符串被稱為“限定字符串”(interned string)。當(dāng)你覺得某個特定的文本會被創(chuàng)建并且反復(fù)在程序中使用多次的話,那可以使用限定字符串。

引擎的API提供了幾種工作于限定字符串的函數(shù)調(diào)用:

  • JS_InternString,
    用來創(chuàng)建或者復(fù)用一個JSString.

  • JS_InternUCString,
    用來創(chuàng)建或者復(fù)用一個統(tǒng)一碼的JSString.

  • JS_InternUCStringN,
    用以創(chuàng)建或者復(fù)用一個固定長度的統(tǒng)一碼JSString。

管理安全性

現(xiàn)在使用JavaScript 1.3,JS引擎加入了安全性增強API函數(shù)來編譯和執(zhí)行傳送給引擎的腳本和函數(shù)。JS安全模型是基于Java的基本安全模型的。該模型提供了一個公共安全接口,但是實際的安全控制由你去實現(xiàn)。

在使用JavaScript的應(yīng)用中使用安全管理的一個常用的方法是比較腳本的來源和限制腳本的交互。例如,你可能會比較兩個或多個腳本的代碼源并且只允許來自相同的代碼源的腳本修改共享代碼源的腳本的屬性。

如要實現(xiàn)安全JS,請按照以下幾步:

  1. 在代碼中聲明一個或多個JSPrincipals類型的結(jié)構(gòu)體(struct)。

  2. 把實現(xiàn)了安全信息的函數(shù)列表添加到數(shù)組中。這些包括了為程序提供原則數(shù)組的函數(shù),和使用給定原則的JS對象的引用計數(shù)增減機制。

  3. JSPrincipals結(jié)構(gòu)和你的安全信息組裝起來。這個信息可以包括一般代碼源信息。

  4. 在運行時,使用一些特定的JS API調(diào)用來編譯和執(zhí)行所有要應(yīng)用安全性的腳本和函數(shù),他們將要求傳遞一個JSPrincipals結(jié)構(gòu)。下面的表格列出了這些API函數(shù)和他們的作用:


    函數(shù)

    目的

    JS_CompileScriptForPrincipals

    編譯(但是不執(zhí)行)一個啟用安全控制的腳本。

    JS_CompileUCScriptForPrincipals

    編譯(但不執(zhí)行)一個啟用安全控制、統(tǒng)一碼編碼的腳本。

    JS_CompileFunctionForPrincipals

    從一個文本串創(chuàng)建一個啟用安全控制的JS函數(shù)。

    JS_CompileUCFunctionForPrincipals

    從一個統(tǒng)一碼編碼的字符串中創(chuàng)建一個帶安全信息的JS函數(shù)。

    JS_EvaluateScriptForPrincipals

    編譯和執(zhí)行一個啟用安全控制的腳本。

    JS_EvaluateUCScriptForPrincipals

    編譯并執(zhí)行一個啟用安全控制且用統(tǒng)一碼編碼的腳本。

    文章來源:url https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_User_Guide

    實現(xiàn)教程:

    SpiderMonkey

    SpiderMonkey是Mozilla項目的一部分,是一個用C語言實現(xiàn)的JavaScript腳本引擎,另外還有一個叫做Rhino的Java版本。

    首先你要下載SpiderMonkey的最新版本。下載包是以源代碼形式分發(fā)的。這就需要你自己編譯腳本引擎。對于Visual C++用戶來說可以在src-目錄找到工程文件。編譯結(jié)果是一個叫作"js32.dll"的動態(tài)庫。SpiderMonkey還能應(yīng)用于
    Macintosh及Unix平臺之上。想了解如何在其他平臺編譯SpiderMonkey請參閱ReadMe.html。

    SpiderMonkey的世界

    為了在SpiderMonkey中運行JavaScript代碼,應(yīng)用程序必須有三個要素:JSRuntime,JSContext和全局對象。

    運行時環(huán)境

    JSRuntime,為其中的JavaScript變量、對象、腳本和應(yīng)用程序中使用的上下文分配空間。每個JSContext和腳本中的每個對象都生活在一個 JSRuntime中。他們不能轉(zhuǎn)移到其他運行時上或在與其它運行時共享。一般來說大多數(shù)應(yīng)用程序只需要一個運行時環(huán)境。

    上下文

    JSContext,就像是一臺小機器,它涉及JavaScript代碼和對象的很多東西。它可以編譯和執(zhí)行腳本、獲取和設(shè)置對象屬性、調(diào)用JavaScript函數(shù)、一種類型轉(zhuǎn)換為另一種JavaScript數(shù)據(jù)、創(chuàng)建對象,等等。幾乎所有JSAPI函數(shù)都要一個JSContext*作為其第一個參數(shù),就像<stdio.h>中的大多數(shù)函數(shù)都需要FILE*一樣.

    全局對象

    全局對象包含所有可以在JavaScript代碼中使用的類、函數(shù)和變量。

    當(dāng)JavaScript代碼要做一些事時,比如window.open("http://www.mozilla.org/"),實際上它是在訪問一個全局屬性(全局對象的屬性),在這里是window。

    腳本能看到的全局屬性完全由應(yīng)用程序控制。應(yīng)用程序首先創(chuàng)建一個對象并加入JavaScript標(biāo)準(zhǔn)類,如Array和Object。然后加入任何程序想加入的自定義的類、函數(shù)和變量(象這里的window)。應(yīng)用程序每次運行js腳本(例如使用JS_EvaluateScript)時提供了該腳本使用的全局對象。至于腳本,它也可以創(chuàng)建自己全局函數(shù)和變量。所有的這些函數(shù)、類和變量都作為屬性存儲在全局對象中。



    在C++中執(zhí)行JavaScript腳本

    第1步 - 創(chuàng)建JavaScript運行時環(huán)境(runtime)

    JavaScript運行時環(huán)境通過調(diào)用JS_NewRuntime來初始化。它負(fù)責(zé)分配運行時環(huán)境所需的內(nèi)存資源。你要指定所分配的字節(jié)數(shù),超過這個大小時碎片收集器將會執(zhí)行。

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

    第2步 - 創(chuàng)建一個上下文(context)

    上下文指定腳本中棧的大小,并分配指定大小的內(nèi)存作為腳本的執(zhí)行棧。每個腳本關(guān)聯(lián)于自己所擁有的上下文。當(dāng)上下文被一個腳本或線程使用時,其他腳本或線程不能使用。當(dāng)腳本或線程結(jié)束,這個上下文就可以被重用于下一個腳本或線程。

    用JS_NewContext方法創(chuàng)建新的上下文。一個上下文和一個運行時環(huán)境關(guān)聯(lián),你必須指定棧的大小。

    JSContext *cx = JS_NewContext(m_rt, 8192); 
    if ( cx == NULL )
    { // Do some error reporting}

    第3步 - 初始化全局對象(global object)

    在腳本執(zhí)行前,你必須初始化JavaScript普通函數(shù)并創(chuàng)建腳本中的大量對象。

    全局對象(global object)用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)建全局對象并初始化它了:

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

    當(dāng)這個腳本運行沒有錯誤,今天的日期被保存在rval中。rval保存函數(shù)最后的執(zhí)行結(jié)果。JS_EvaluateScript的返回值,運行成功是 JS_TRUE,發(fā)生錯誤是 JS_FALSE 。

    從rval取回字符串的值如下所示。這里就不演示每個細(xì)節(jié). 當(dāng)你需要的時候查看相關(guān) API 的信息。

    JSString *str = JS_ValueToString(cx, rval);
    std::cout << JS_GetStringBytes(str);

    第5步 - 清理腳本引擎

    在應(yīng)用程序結(jié)束前, 必須清理腳本引擎.

    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對象

    創(chuàng)建一個允許被JavaScript腳本使用的C++類。

    通過JSClass結(jié)構(gòu)來定義JavaScript對象。我們要把它作為公共靜態(tài)(public static)成員,因為這個結(jié)構(gòu)要被其他類使用。另外它還可以被用于其他類來確定對象類型。(參考 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ù)名。例如當(dāng)引擎需要從你的類中獲取一個屬性時回調(diào)。

    在C++文件的實現(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)是 JSCustomer::JSGetProperty, JSCustomer::JSSetProperty 和 JSCustomer::JSDestructor。JSGetProperty 當(dāng)引擎獲取屬性時被回調(diào), JSSetProperty 當(dāng)引擎設(shè)置屬性時被回調(diào),JSDestructor 當(dāng)JavaScript 對象被銷毀時回調(diào)。

    標(biāo)志 JSCLASS_HAS_PRIVATE 用于指示引擎開辟內(nèi)存來綁定數(shù)據(jù)到 JavaScript 對象. 你可以用this存儲一個指向你的對象的指針.

    回調(diào)是靜態(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對象

    創(chuàng)建另一個叫 JSInit 的靜態(tài)方法 ,見下例. 這個方法將被應(yīng)用程序調(diào)用,用來創(chuàng)建 JavaScript 運行時環(huán)境.

    static JSObject *JSInit(JSContext *cx, JSObject *obj, JSObject *proto);

    實現(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;
    }

    靜態(tài)方法JSConstructor,當(dāng)你的對象被腳本實例化的時候被調(diào)用。使用這個方法可以非常方便地把你的數(shù)據(jù)綁定到對象上,通過調(diào)用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;
    }

    這個構(gòu)造器方法可以有多個參數(shù), 能用于初始化你的類. 現(xiàn)在你已經(jīng)在堆上創(chuàng)建了一個指針, 你也需要一種途徑銷毀這個指針. 這可以通過靜態(tài)方法 JS_Destructor 來完成.

    void JSCustomer::JSDestructor(JSContext *cx, JSObject *obj)
    {
    	JSCustomer *p = JS_GetPrivate(cx, obj);	delete p;
    	p = NULL;
    }

    第3步 - 增加屬性

    增加一個類型為JSPropertySpec的靜態(tài)數(shù)組成員。這個數(shù)組將包含屬性信息。再創(chuàng)建一個屬性ID號的枚舉(enum).

    static JSPropertySpec customer_properties[]; 
    enum{name_prop,age_prop};

    在實現(xiàn)文件中初始化這個數(shù)組,代碼如下

    JSPropertySpec JSCustomer::customer_properties[] = 
    { 
    	{ "name", name_prop, JSPROP_ENUMERATE },
    	{ "age", age_prop, JSPROP_ENUMERATE },
    	{ 0 }
    };

    數(shù)組的最后一個元素必須是空(NULL). 每個元素包含另一個有 3 個元素的數(shù)組. 第一個元素是名字,將在 JavaScript 腳本中使用。第二個元素是屬性的唯一ID號, 將被傳遞到回調(diào)函數(shù)中。第三個元素是一個標(biāo)志,JSPROP_ENUMERATE 表示腳本中當(dāng)枚舉Customer對象的這個屬性時是可見的,就是可以用在腳本中的for 或 in 語句中. 你可以指定 JSPROP_READONLY 屬性來說明這個屬性是不可以修改的.

    現(xiàn)在是時候?qū)崿F(xiàn)獲取(getting)和設(shè)置(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)中返回 JS_TRUE。當(dāng)你返回 JS_FALSE 將表示在你的對象中沒有發(fā)現(xiàn)這個屬性.

    第4步 - 增加方法

    創(chuàng)建類型為JSFunctionSpec的靜態(tài)成員數(shù)組.

    static JSFunctionSpec customer_methods[];

    在實現(xiàn)文件中初始化這個數(shù)組,代碼如下

    JSFunctionSpec wxJSFrame::wxFrame_methods[] = 
    {
    	{ "computeReduction", computeReduction, 1, 0, 0 },
    	{ 0 }
    };

    數(shù)組的最后一個元素必須是空(NULL). 每個元素包含另一個有 5 個元素的數(shù)組. 第一個元素是腳本中使用的函數(shù)名. 第二個元素是全局或靜態(tài)成員函數(shù)名. 第三個元素是這個函數(shù)的參數(shù)個數(shù). 最后兩個元素忽略.

    在類中創(chuàng)建一個靜態(tài)方法

    static JSBool computeReduction(JSContext *cx, JSObject *obj, uintN argc,
    	jsval *argv, jsval *rval);

    當(dāng)函數(shù)成功就返回 JS_TRUE,否則返回 JS_FALSE。 你的JavaScript方法實際返回值被放到了rval參數(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)建的對象

    var c = new Customer(); 
    c.name = "Franky";
    c.age = 32;
    var reduction = c.computeReduction();

    不要忘記當(dāng)創(chuàng)建上下文的時候初始化JavaScript對象:

    JSObject *obj = JSCustomer::JSInit(cx, global);




    代碼

    //main.cpp 演示如何執(zhí)行javascript 
    #define XP_PC#include <string>#include <iostream>#include <fstream>#include <jsapi.h>#include "JSCustomer.h"JSClass globalClass = {"Global",0,
        JS_PropertyStub,
        JS_PropertyStub,
        JS_PropertyStub,
        JS_PropertyStub,
        JS_EnumerateStub,
        JS_ResolveStub,
        JS_ConvertStub,
        JS_FinalizeStub
    };void main(int argc, char *argv[])
    {if ( argc < 2 )
        {
            std::cout << "JSExec usage" << std::endl
                << "------------" << std::endl
                << "JSExec <fileName>" << std::endl;
        }
        std::string script;
        std::string buffer;
        std::ifstream istr(argv[1]);if ( istr.is_open() )
        {do{
                std::getline(istr, buffer);
                script += buffer;
            } while (!istr.fail());
        }else{
            std::cout << "JSExec error" << std::endl
                << "------------" << std::endl
                << "Can't open scriptfile " << argv[1] << std::endl;
            exit(0);
        }
        JSRuntime *rt = JS_Init(1000000L);if ( rt )
        {
            JSContext *cx = JS_NewContext(rt, 8192);if ( cx )
            {
                JSObject *globalObj = JS_NewObject(cx, &globalClass, 0, 0);if ( globalObj )
                {
                    JS_InitStandardClasses(cx, globalObj);// Init JSCustomerJSCustomer::JSInit(cx, globalObj);// Execute the scriptjsval rval;
                    uintN lineno = 0;
                    JSString *str;
                    JSBool ok = JS_EvaluateScript(cx, globalObj, script.c_str(),
                        script.length(), argv[1], lineno, &rval);if ( ok == JS_TRUE )
                    {
                        str = JS_ValueToString(cx, rval);char *s = JS_GetStringBytes(str);
                        std::cout << "JSExec result" << std::endl
                            << "-------------" << std::endl
                            << s << std::endl;
                    }else{
                        std::cout << "JSExec error" << std::endl
                            << "------------" << std::endl
                            << "Error in JavaScript file " << argv[1] << std::endl;
                    }
                }else{
                    std::cout << "Unable to create the global object";
                }
                JS_DestroyContext(cx);
            }else{
                std::cout << "Unable to create a context";
            }
            JS_Finish(rt);
        }else{
            std::cout << "Unable to initialize the JavaScript Engine";
        }
    }

    //JSCustomer.h 演示Customer JavaScript類的定義 
    /*** JSCustomer.h - Example for my tutorial : Scripting C++ with JavaScript
     * (c) 2002 - Franky Braem
     * http://www.braem17.yucom.be
     */#ifndef _JSCustomer_H#define _JSCustomer_H#include "Customer.h"class JSCustomer
    {public:	/*** Constructor*/
    	JSCustomer() 
    	: m_pCustomer(NULL){}	/*** Destructor*/virtual ~JSCustomer()
    	{	delete m_pCustomer;
    	m_pCustomer = NULL;
    	}	/*** JSGetProperty - Callback for retrieving properties*/static JSBool JSGetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);	/*** JSSetProperty - Callback for setting properties*/static JSBool JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);	/*** JSConstructor - Callback for when a wxCustomer object is created*/static JSBool JSConstructor(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);	/*** JSDestructor - Callback for when a wxCustomer object is destroyed*/static void JSDestructor(JSContext *cx, JSObject *obj);	/*** JSInit - Create a prototype for wxCustomer*/static JSObject* JSInit(JSContext *cx, JSObject *obj, JSObject *proto = NULL);	static JSBool computeReduction(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);	static JSClass Customer_class;	void setCustomer(Customer *customer){m_pCustomer = customer; }
    	Customer* getCustomer() {return m_pCustomer; }private:
    	Customer *m_pCustomer;	static JSPropertySpec Customer_properties[];	static JSFunctionSpec Customer_methods[];	enum{name_prop,age_prop};
    };#endif //_JSCustomer_H

    //JSCustomer.cpp 演示JSCustomer類的實現(xiàn)/*** JSCustomer.cpp - Example for my tutorial : Scripting C++ with JavaScript
     * (c) 2002 - Franky Braem
     * http://www.braem17.yucom.be
     */#include <string>#define XP_PC#include <jsapi.h>//#include "Customer.h"#include "JSCustomer.h"JSPropertySpec JSCustomer::Customer_properties[] = { 
        { "name", name_prop, JSPROP_ENUMERATE },
        { "age", age_prop, JSPROP_ENUMERATE },
        { 0 }
    };
    JSFunctionSpec JSCustomer::Customer_methods[] = {
        { "computeReduction", computeReduction, 1, 0, 0 },
        { 0, 0, 0, 0, 0 }
    };
    JSClass JSCustomer::Customer_class = { "Customer",
        JSCLASS_HAS_PRIVATE,
        JS_PropertyStub,
        JS_PropertyStub,
        JSCustomer::JSGetProperty,
        JSCustomer::JSSetProperty,
        JS_EnumerateStub,
        JS_ResolveStub,
        JS_ConvertStub,
        JSCustomer::JSDestructor
    };
    JSBool JSCustomer::JSGetProperty(JSContext *cx, JSObject *obj, jsval id,jsval *vp)
    {if (JSVAL_IS_INT(id)) 
        {
            JSCustomer *p = (JSCustomer *) JS_GetPrivate(cx, obj);
            Customer *customer = p->getCustomer();switch (JSVAL_TO_INT(id))
            {case name_prop:
                    {
                        std::string name = customer->GetName();
                        JSString *str = JS_NewStringCopyN(cx, name.c_str(), name.length());
                        *vp = STRING_TO_JSVAL(str);break;
                    }case age_prop:
                    *vp = INT_TO_JSVAL(customer->GetAge());break;
            }
        }return JS_TRUE;
    }
    JSBool JSCustomer::JSSetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
    {if (JSVAL_IS_INT(id))
        {
            JSCustomer *p = (JSCustomer *) JS_GetPrivate(cx, obj);
            Customer *customer = p->getCustomer();switch (JSVAL_TO_INT(id))
            {case name_prop:
                    {
                        JSString *str = JS_ValueToString(cx, *vp);
                        std::string name = JS_GetStringBytes(str);
                        customer->SetName(name);break;
                    }case age_prop:
                    customer->SetAge(JSVAL_TO_INT(*vp));break;
            }
        }return JS_TRUE;
    }
    JSBool JSCustomer::JSConstructor(JSContext *cx, JSObject *obj, uintN argc,
            jsval *argv, jsval *rval)
    {
        JSCustomer *priv = new JSCustomer();
        priv->setCustomer(new Customer());
        JS_SetPrivate(cx, obj, (void *) priv);return JS_TRUE;
    }void JSCustomer::JSDestructor(JSContext *cx, JSObject *obj)
    {
        JSCustomer *priv = (JSCustomer*) JS_GetPrivate(cx, obj);delete priv;
        priv = NULL;
    }
    JSObject *JSCustomer::JSInit(JSContext *cx, JSObject *obj, JSObject *proto)
    {
        JSObject *newProtoObj = JS_InitClass(cx, obj, proto, 
                &Customer_class, JSCustomer::JSConstructor, 0,NULL, JSCustomer::Customer_methods,NULL, NULL);
        JS_DefineProperties(cx, newProtoObj, JSCustomer::Customer_properties);return newProtoObj;
    }
    JSBool JSCustomer::computeReduction(JSContext *cx, JSObject *obj, uintN argc,
            jsval *argv, jsval *rval)
    {
        JSCustomer *p = (JSCustomer*) JS_GetPrivate(cx, obj);if ( p->getCustomer()->GetAge() < 25 )
            *rval = INT_TO_JSVAL(10);else*rval = INT_TO_JSVAL(5);return JS_TRUE;
    }

    //Customer.h 演示Customer C++類的定義#ifndef _Customer_H#define _Customer_Hclass 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;
    };#endif

    //example.js 演示JavaScript的例子 
    var c = new Customer();
    c.name = "Franky";
    c.age = 32;
    c.computeReduction();



看完上述內(nèi)容,你們掌握怎么進行JavaScript-C/C++引擎嵌入開發(fā)的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

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

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI