搜索
您的当前位置:首页正文

JSAPI用户手册

来源:筏尚旅游网
JSAPI⽤户⼿册

本⽂档主要涵盖如何嵌⼊SpiderMonkey javascript引擎到你⾃⼰的c++程序中。

JavaScript在浏览器端已经被⼴泛使⽤了。但是,Mozilla的javascript引擎可以被嵌⼊到任何c++程序中,⽽不仅仅是应⽤于浏览器。许多应⽤程序开发可以通过脚本化的⽅式获益,这些程序可以使⽤SpiderMonkey API让c++代码⾥⾯跑js代码。What SpiderMonkey does?

Javascript引擎编译并执⾏js脚本。引擎本⾝负责脚本执⾏时的对象内存分配,垃圾收集等⼯作。

SpiderMonkey⽀持Javascript 1.0-1.8版本。Js 1.3以及后来的版本都符合ECMAScript 262-3规范。后⾯的版本也包含Mozilla扩展,⽐如数组压缩(array comprehensions)和⽣成器(generators).SpiderMonkey也⽀持E4X,但是这个是可选的。

在Javascript的世界⾥⾯,我们马上就可以想到许多特性,⽐如事件处理(onclick),DOM对象,window.open以及XMLHTTPRequest.但是,在Mozilla⾥⾯所有的特性都是由另外的组件提供,⽽不是SpiderMonkey引擎本⾝。SpiderMonkey引擎本⾝只提供javascript核⼼数据类型,⽐如number,string,array,object等。还有⼀些常⽤的⽅法,⽐如Array.push。SpiderMonkey还可以让其它程序⾮常⽅便地暴露⾃⼰程序中的对象和接⼝给js代码。⽐如,浏览器暴露的就是DOM接⼝。(cocos2d-x暴露的是cocos2d-x的接⼝)。你⾃⼰的程序也可以根据脚本的需求来设计待暴露接⼝。具体由程序开发者⾃⼰来决定哪些对象和⽅法要暴露给脚本。Hello World

Using the SpiderMonkey library

你的程序可以像使⽤任何其它c++程序库⼀样来使⽤SpiderMonkey。如果想从源码构建SpiderMonkey,可以参考.你也可以把SpiderMonkey当作⼀个静态库或者动态库集成进来。

有⼀些平台上⾯(⽐如Debian linux)内置了SpiderMonkey包。这样⼦安装会变得⾮常容易,但是调试可能会⽐较复杂。⾥⾯也包含⼀个SpiderMonkey引擎,同时还有头⽂件和库⽂件。

c++代码通过JSAPI来访问SpiderMonkey,通过导⼊头⽂件”jsapi.h”。JSAPI提供了相应的函数来建⽴javascript运⾏时环境,编译,执⾏脚本,创建和访问javascript数据结构,处理错误,允许安全性检查以及调试脚本。

如果想了解JSAPI的全部能⼒,你可以查看;The SpiderMonkey universe

如果想在SpiderMonkey⾥⾯跑javascript,⼀个程序必须有三个关键元素:⼀个JSRutime,⼀个JSContext和⼀个全局对象。这⼀⼩节专门来阐述这三个关键元素。下⼀节主要是介绍怎么使⽤JSAPI来配置这三个关键元素。

Runtimes ⼀个JSRutime,或者说运⾏时。你的程序⾥所有的js变量,对象,脚本和上下⽂都由它来分配内存。每⼀个JSContex和每⼀个js对象都依托于JSRuntime。它们不能越界访问,也不能共享资源。⼤部分应⽤程序只需要⼀个runtime就可以了。

Contexts⼀个JSContext,或者说上下⽂。它是⼀个⼩的虚拟机,但是可以完成许多javascript对象相关的任务。它负责编译和执⾏脚本,设置和访问js对象的属性,调⽤js函数,把js数据类型转换成其它数据类型,创建对象等等。⼏乎所有的JSAPI都需要⼀个JSContext 对象作为第⼀个参数。就像⾥⾯的⽂件操作⼀样,都需要⼀个FILE*指针。

在context和线程之间还有⼀层亲密关系。简单来说,单线程的程序可以使⽤⼀个context就够了。但是,⼀个context⼀次只能做⼀件事情。因此,在⼀个多线程程序中,⼀个线程在任何时刻应该可以使⽤任意给定的context。那样的程序,⼀般来说会设计成每⼀个线程拥有⾃⼰的context。js对象,从另⼀个⾓度来说,它并不是⼀直与script、thread和创建它的context同⽣共死的。它们可以被许多脚本或者许多线程所共⽤。如图1。1所⽰:Figure 1.1 js脚本、上下⽂和对象的关系A minimal example

在上⼀节中描述的三种关键元素需要相应的JSAPI调⽤:

*The runtime:使⽤JS_NewRuntime可以创建⼀个js runtime,对应的可以使⽤ JS_DestroyRuntime来销毁这个runtime。当你的程序⾥⾯集成SpiderMonnkey以后,你可以使⽤JS_ShutDown来释放掉缓存⾥⾯的资源。(虽然说,你的程序退出的时候这些缓存的资源都会被释放,⼿动调⽤JS_ShutDown显得有点多此⼀举。但是,调⽤它是⼀个好习惯。)

*The context: 使⽤JS_NewContext和JS_DestroyContext。为了最⼤化兼容ECMAScript标准,应⽤程序需要调⽤JS_SetOptions来激活JSOPTION_VAROBJFIX.为了⽀持最新的js语⾔特性,应⽤程序需要调⽤JS_SetVersion。错误报告也是⼀个context,我们可以⽤JS_SetErrorReporter来⽀持它。

*The global object: 为了创建这个对象,你⾸先需要采⽤JSCLASS_GLOBAL_FLAGS来创建⼀个JSClass。下⾯的例⼦中,我们举了⼀个⾮常简单的JSClass(叫做global_class)。它本⾝没有任何属性和⽅法。使⽤JS_NewGlobalObject来创建⼀个全局对象。使⽤JS_InitStandardClasses⽅法来操作它。下⾯的代码向我们展⽰了⼀个最简化版的JSAPI应⽤程序。它包含了我们之前介绍的所有知识点:

123456

7#include \"jsapi.h\"

8/* The class of the global object. */

9static JSClass global_class = { \"global\

10 JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,11 JS_PropertyStub,

12 JS_DeletePropertyStub,13 JS_PropertyStub,

14 JS_StrictPropertyStub,15 JS_EnumerateStub,16 JS_ResolveStub,17 JS_ConvertStub,18 NULL,

19 JSCLASS_NO_OPTIONAL_MEMBERS20};

21/* The error reporter callback. */

22void reportError(JSContext *cx, const char *message, JSErrorReport *report) {23 fprintf(stderr, \"%s:%u:%s\\n\

24 report->filename ? report->filename : \"\25 (unsigned int) report->lineno,26 message);27}

28int run(JSContext *cx) {

29 /* Enter a request before running anything in the context */30 JSAutoRequest ar(cx);

31 /* Create the global object in a new compartment. */

32 JSObject *global = JS_NewGlobalObject(cx, &global_class, NULL);33 if (global == NULL)34 return 1;

35 /* Set the context's global */

36 JSAutoCompartment ac(cx, global);37 JS_SetGlobalObject(cx, global);

38 /* Populate the global object with the standard globals, like Object and Array. */39 if (!JS_InitStandardClasses(cx, global))40 return 1;

41 /* Your application code here. This may include JSAPI calls to create your own custom JS objects and run scripts. */42 return 0;43}

44int main(int argc, const char *argv[]) {45 /* Create a JS runtime. */

46 JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L, JS_NO_HELPER_THREADS);47 if (rt == NULL)48 return 1;

49 /* Create a context. */

50 JSContext *cx = JS_NewContext(rt, 8192);51 if (cx == NULL)52 return 1;

53 JS_SetOptions(cx, JSOPTION_VAROBJFIX);54 JS_SetErrorReporter(cx, reportError);55 int status = run(cx);56 JS_DestroyContext(cx);57 JS_DestroyRuntime(rt);58 JS_ShutDown();59 return status;60}616263646566

每⼀个JSNative有⼀样的签名,完全可以忽视Javascript那边所传递的函数参数。

传递给javascript函数的参数由argc和vp.argc来计算,⼀共传递了多少个参数。然后使⽤JS_ARGV(cx,cp)来返回这些参数的⼀个数组。这些参数没有基础c++类型,⽐如init、float之类的。它们都是⼀些jsval,即javascript值。这些native的函数使⽤JS_ConvertArgument来把这些jsval转换成相应的c++类型,然后再把它们存储在本地局部变量中。native函数还使⽤JS_SET_RVAL(cx,vp,val)来把c++的值返回给js端。

当函数调⽤成功的时候,⼀个JSNative必须调⽤JS_SET_RVAL,并且返回⼀个JS_TRUE。传递给JS_SET_RVAL的值最终被返回给js端。

当函数调⽤失败的时候,⼀个JSNative会调⽤⼀个错误处理函数,在本例中就是JS_ReportError,并且返回JS_FALSE。这个代码调⽤会导致javascript异常被触发。调⽤者可以在javascript代码⾥⾯使⽤try/catch来捕获这些异常。

要让⼀个native的函数可以被js端调⽤,我们需要声明⼀个JSFunctionSpec表来描述这些函数信息。然后调⽤JS_DefineFunction。1234567

static JSFunctionSpec myjs_global_functions[] = { JS_FS(\"rand\ JS_FS(\"srand\ JS_FS(\"system\ JS_FS_END};

8 ...

9 if (!JS_DefineFunctions(cx, global, myjs_global_functions))10 return JS_FALSE;11 ...

1system(\"echo hello world\");

⼀旦函数被定义在global中,任何脚本使⽤global作为全局对象都可以调⽤它,就像每⼀个web页⾯可以调⽤alert函数⼀样。在上⾯的配置中,我们可以使⽤下⾯的脚本来跑”Hello World”:

JSAPI Concepts

该部分主要任务是填JSAPI的坑。如果你想使⽤SpiderMonkey来开发什么程序的话,这必须仔细认真细致完整反复地阅读本节的3个部分内容。Javascript values主要的⽂章:

Javascript是⼀种动态类型的语⾔:所有的变量和属性在编译间都是没有类型的。那么,像c、c++这种静态类型语⾔怎么同js交互呢?JSAPI提供了⼀种数据类型叫做jsval,它可以代表js⾥⾯的任何数据类型。⼀个jsval可以是⼀个数字,⼀个字符串,⼀个bool值,⼀个对象的引⽤(⽐如Object,Array,Data或者Function),甚⾄可以是null或者undefined。对于整形和bool值,jsval⾃⾝是包含其值的。在其它情况下,jsval只是⼀个指针 ,要么指向⼀个对象或者⼀个数组 。

1

温馨提⽰:就像c++的指针⼀样,⼀个jsval它不会⾃动初始化为⼀个安全的值,也可能会成为⼀个野指针 。这⼀点和js中的var是有区别的。2

⼀个悬挂的指针过去可能指向的是⼀个有效的地址,但是也有可能不会。使⽤悬挂指针会导致c++程序崩溃。就jsval⽽⾔,javascript⾥⾯的垃圾收集器会⾃动收集程序⾥⾯可回收的对象,字符串数字等对象。但是jsval对象本⾝,它并不保证其⾃⾝3

collection那⼀节来了解更多有关jsval安全性的细节问题。4

JSAPI⾥⾯包含了⼀些宏,可以⽤来测试jsval的javascriopt数据类型。它们是:1JSVAL_IS_OBJECT2JSVAL_IS_NUMBER3JSVAL_IS_INT

4JSVAL_IS_DOUBLE5JSVAL_IS_STRING6JSVAL_IS_BOOLEAN7JSVAL_IS_NULL8JSVAL_IS_VOID

⼀个jsval指向⼀个JSObject,jsdouble或者JSString对象。你可以把jsval向下转型成你需要的类型。转换的api接⼝是JSVAL_TO_OBJECT,JSVAL_TO_DOUBLE和JSVAL_TO_STRING。这些api可以帮助我们在需要特性类型的时候去做相应的数据类型转换。类似的,你也可以把⼀个JSObjectt,jsdouble或者JSString对象指针转换成⼀个jsval。通过使⽤OBJECT_TO_JSVAL,DOUBLE_TO_JSVAL和STRING_TO_JSVAL.Gabage collection

⼀旦js脚本跑起来以后,所有的js对象,字符串和变量等数据结构都会分配内存。垃圾收集指的是js引擎会⾃动检测哪些内存没有被引⽤,⽽且也不再可能被再次使⽤,最终js引擎会⾃动回收这部分内存。

垃圾收集对于⼀个JSAPI的程序来讲有两个⾮常重⼤的影响。⾸先,应⽤程序需要保证js⾥⾯的任何值都是可以被GC的。垃圾收集器是会很积极地去搜寻没有被引⽤的内存的,⼀旦发现有这样的内存块,它就会释放之。其次,应⽤程序需要考虑垃圾收集器对程序性能的影响。Keeping objects live

如果你的JSAPI程序crash了,很可能是由于垃圾收集器发⽣错误了。你的程序必须确保垃圾收集器可以访问到所有正常使⽤的js变量。否则,没有被gc引⽤的内存就会被gc释放掉。因为你的程序下次可能会使⽤到这些被释放的内存,此时,crash发⽣了。我们有许多⽅法可以保证⼀个值是可以被gc管理到的:

如果你想⼀个js value在JSNative调⽤期间都可以被访问,你可以把它存储到*rval或者⼀个argv数组中。任何值存储在这两个地⽅都可以被访问到。如果还想使⽤更多的argv槽,可以使⽤JSFunctionSpec.extra.

如果⼀个定制的对象需要把某些值存储在内存中,只需要把这些值当作此对象的属性存储起来即可。只要这个对象是可以被引⽤的,那么它的属性就是可以被访问到的。如果这些值不⼀定要让js代码访问,可以使⽤保留槽。 也可以使⽤⼀个JSClass.mark,然后把值存储到中。

如果⼀个函数创建新的对象、字符串和数字,它可以使⽤JS_EnterLocalRootScope和JS_LeaveLocalRootScope来保证这些值在函数调⽤期间不被释放。如果想让⼀个值永久存在,那么可以它把存储到中。

但是,GC bug还是有可能会发⽣的。这两个函数,都只有在debug模式下⾯才能使⽤,它们对于debug和gc相关的crash⾮常有帮助。

使⽤来激活另外⼀个垃圾收集器。GC zeal会让GC相关的crash暴露地更多,⽽且富含更多信息。这个只能⽤于程序开发和debug阶段,因为这个额外的gc会让js跑得⾮常慢。使⽤来把SpiderMonkey的堆和其它有⽤的信息dump出来。你可以参考来了解更多信息。GC performance

如果经常去做⾃动垃圾收集,会对你的程序性能造成⽐较⼤的影响。有⼀些程序可以通过增加JSRuntime的初始值⼤⼩来减少GC的频率。

不过,最好的技术应该是让程序在空闲的时候去做GC,这种情况下⾯,它对终端⽤户的影响最⼩。默认情况下,js引擎的垃圾收集时机是,它除了⾃动增长进程空间⽽别⽆他法时。这句话的意思是,垃圾收集发⽣在内存很吃紧的时候,但是,这时候做垃圾收集其实也是最坏的时机。⼀个应⽤程序可以通过调⽤JS_GC和JS_MaybeGC两个函数来触发垃圾收集。JS_GC会强制执⾏垃圾收集。⽽JS_MaybeGC会提醒垃圾收集器,您⽼是不是该收集⽆⽤的内存资源啦?Errors and exceptions

检查⼀个JSAPI函数的返回值的重要性是⽆需多⾔的。因为每⼀个JSAPI函数都会使⽤⼀个JSContext指针作为参数,这有可能会导致函数调⽤失败。因为系统可能会出现内存耗尽的问题。也有可能js脚本中存在语法错误。还有,脚本中有可能显⽰throw⼀个异常。

因为Javascript语⾔本⾝⽀持异常,⽽c++也⽀持异常,它两肯定不是⼀码事。SpiderMonkey本⾝并没有使⽤任何c++异常。JSAPI也不会抛出c++异常,当SpiderMonkey去调⽤⼀个应⽤程序回调时,这个回调也绝不会抛出⼀个c++异常。Throwing and catching exceptions

我们已经看到⼀个JSNative函数中如何抛出异常的例⼦了。只需要简单地调⽤JS_ReportError,然后使⽤⼀个和printf风格的参数列表并且返回JS_FALSE。1rc = system(cmd);2if (rc != 0) {

3 /* Throw a JavaScript exception. */

4 JS_ReportError(cx, \"Command failed with exit code %d\5 return JS_FALSE;6}

这个和js语句throw new Error(“Command failed with exit code” + rc)⾮常相似。另外,需要注意的是,调⽤JS_ReportError并不会产⽣⼀个c++异常。但是SpiderMonkey的栈在展开时不会把c++函数从栈中移除。SpiderMonkey的做法是,返回⼀个JS_FALSE或者NULL给应⽤。如果想了解更多有关异常抛出和异常处理的例⼦,可以参考.Error reports

这些家伙很懒,这部分⽂档没有。

Automatic handling of uncaught exceptions

在某些特定情况下⾯,JS_Compile,JS_Call,JS_Execute和JS_Evaluate函数会⾃动把异常信息传递给error reporter程序。这些函数在执⾏的时候,都会事先检查JSContext,看其中是否有异常。如果有,那么它们会继续检查是否还有其它js代码或者js函数在此JSContext中。如果有,那么这些异常可能会被捕捉,这时SpiderMonkey什么也没做只返回JS_FALSE,并且让异常可以被传播。但是,如果js栈上什么都没有的话,那么没有被捕获的异常就会被直接传给error reporter。

最后的结果就是,顶层的应⽤程序设置⼀个error reporter,然后再开始调⽤JSAPI函数。应⽤程序永远都不需要显式去处理未捕获的异常消息。因为error reporter会⾃动被调⽤。应⽤程序也可以禁⽤这项功能,通过使⽤(JSOPTION_DONT_REPORT_UNCAUGHT),但是,如果这样做,你还需要显式地调⽤JS_IsExceptioinPending, JS_GetPendingException, JS_ReportPendingException和JS_ClearPendingException,调⽤时机就是在JSAPI函数返回JS_FALSE或者NULL之前。Uncatchable errors

还有⼀种⽅式来让JSNative回调来报出⼀个错误:1if (p == NULL) {

2 JS_ReportOutOfMemory(cx);3 return JS_FALSE;4}

这⾥的代码和JS_ReportError所做的略微有些不同。

⼤部分的错误,包括由JS_ReportError所抛出的错误,都可以⽤javascript语⾔异常来表⽰。使⽤js的⼀些内置api try/catch/finally。但是,有时候,我们并不想让js端去catch某⼀个错误,⽽是想让脚本直接就终⽌运⾏。如果在脚本执⾏期间,我们的系统内存耗尽了,我们就不想让finally那部分代码执⾏了。因为,脚本总需要⼀点点内存来执⾏,⽽此时我们已经没有内存了。如果⼀个脚本已经运⾏很长时间了,我们想杀死它,这时候就不能产⽣异常,因为那样的话js端的catch会捕捉到这个异常并继续执⾏下去。因此,JS_ReportOutOfMemory(cx)函数被不会抛出⼀个异常,它会产⽣⼀个不可被捕捉的错误。

如果SpiderMonkey把内存耗尽了,或者是⼀个JSAPI回调返回JS_FALSE但是没有附带⼀个异常,那么我们就把它当作⼀个不可被捕捉的错误。此时,js代码的栈会展开,同时js代码中的catch和finally也不会被执⾏。

⼀个不可被捕捉的错误可以让JSContext状态良好。这样JSContext可以被重⽤。应⽤程序也不需要额外的操作来从这些错误中恢复。下⾯⼀段代码向我们演⽰了如何产⽣⼀个不可被捕捉的错误:1

/* Call the error reporter, if any. This part is optional. */2

JS_ReportError(cx, \"The server room is on fire!\");3

JS_ReportPendingException(cx);4

/* Make sure the error is uncatchable. */5

JS_ClearPendingException(cx);6

return JS_FALSE;7

More sample code

下⾯的例⼦使⽤JSAPI来实现了⼀些功能。

注意,最重要的例⼦还是“A minimal example”⼀节中的例⼦。⼤部分的JSAPI代码样例可以在找到。Defining objects and properties

1234

5/* Statically initialize a class to make \"one-off\" objects. */6JSClass my_class = {7 \"MyClass\

8 /* All of these can be replaced with the corresponding JS_*Stub9 function pointers. */

10 my_addProperty, my_delProperty, my_getProperty, my_setProperty,11 my_enumerate, my_resolve, my_convert, my_finalize12};

13JSObject *obj;14/*

15 * Define an object named in the global scope that can be enumerated by16 * for/in loops. The parent object is passed as the second argument, as17 * with all other API calls that take an object/name pair. The prototype18 * passed in is null, so the default object prototype will be used.19 */

20obj = JS_DefineObject(cx, globalObj, \"myObject\21 JSPROP_ENUMERATE);22/*

23 * Define a bunch of properties with a JSPropertySpec array statically24 * initialized and terminated with a null-name entry. Besides its name,

25 * each property has a \"tiny\" identifier (MY_COLOR, e.g.) that can be used26 * in switch statements (in a common my_getProperty function, for example).27 */

28enum my_tinyid {

29 MY_COLOR, MY_HEIGHT, MY_WIDTH, MY_FUNNY, MY_ARRAY, MY_RDONLY30};

31static JSPropertySpec my_props[] = {

32 {\"color\33 {\"height\34 {\"width\35 {\"funny\36 {\"array\37 {\"rdonly\38 {0}39};

40JS_DefineProperties(cx, obj, my_props);41/*

42 * Given the above definitions and call to JS_DefineProperties, obj will43 * need this sort of \"getter\" method in its class (my_class, above). See44 * the example for the \"It\" class in js.c.45 */

46static JSBool

47my_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)48{

49 if (JSVAL_IS_INT(id)) {

50 switch (JSVAL_TO_INT(id)) {

51 case MY_COLOR: *vp = . . .; break;52 case MY_HEIGHT: *vp = . . .; break;53 case MY_WIDTH: *vp = . . .; break;54 case MY_FUNNY: *vp = . . .; break;55 case MY_ARRAY: *vp = . . .; break;56 case MY_RDONLY: *vp = . . .; break;57 }58 }

59 return JS_TRUE;60}616263

Definning classes

这⼀部分内容通过使⽤JSAPI来定义构造函数,原型对象,原型对象的属性,以及构造函数的属性。

初始化⼀个类,可以通过定义构造函数,原型和⼀些预定义的实例属性和类属性。类属性和java中的静态属性有点相似。它们都被定义在对象的构造函数作⽤域内,这样MyClass.myStaticProp就可以和new MyClass()⼀起被js端所使⽤了。

接收很多参数,但是,最后4个参数是可以传递空的。如果你不想让定义的类有相应的属性的话。

注意,你不需要去调⽤JS_InitClass来创建⼀个类的实例,这是⼀个先有鸡还是先有蛋的问题。你只需要调⽤JS_InitClass,这样脚本在执⾏new操作的时候就可以找到相应的构造函数了。这样js端就可以通过运⾏时从对象的prototype对象(MYClass.prototypea)或者继承的对象中访问相应的属性。⼀般来讲,如果你想让多个实例共享同样的⾏为,可以使⽤JS_InitClass.12

protoObj = JS_InitClass(cx, globalObj, NULL, &my_class,3

/* native constructor function and min arg count */4

MyClass, 0,5

/* prototype object properties and methods -- these6

will be \"inherited\" by all instances through7

delegation up the instance's prototype link. */8

my_props, my_methods,9

/* class constructor properties and methods */10

my_static_props, my_static_methods);1112

Running scripts

123

/* These should indicate source location for diagnostics. */4

char *filename;5

uintN lineno;6/*7

* The return value comes back here -- if it could be a GC thing, you must8

* add it to the GC's \"root set\" with JS_AddRoot(cx, &thing) where thing9

* is a JSString *, JSObject *, or jsdouble *, and remove the root before10

* rval goes out of scope, or when rval is no longer needed.11 */12

jsval rval;13

JSBool ok;14/*15

* Some example source in a C string. Larger, non-null-terminated buffers16

* can be used, if you pass the buffer length to JS_EvaluateScript.17 */18

char *source = \"x * f(y)\";19

ok = JS_EvaluateScript(cx, globalObj, source, strlen(source),20

filename, lineno, &rval);21

if (ok) {22

/* Should get a number back from the example source. */23

jsdouble d;24

ok = JS_ValueToNumber(cx, rval, &d);25

. . .26}272829

Calling functions

1

2/* Call a global function named \"foo\" that takes no arguments. */3ok = JS_CallFunctionName(cx, globalObj, \"foo\4jsval argv[2];

5/* Call a function in obj's scope named \"method\6argv[0] = . . .;7argv[1] = . . .;

8ok = JS_CallFunctionName(cx, obj, \"method\9

JSContext

因为在分配和维护context时存在⼀定的开销,⼀个JSAPI程序应该:⼀次只创建需要个数的context。(简⾔之,就是按需创建)

尽可能保持context存活时间更长,⽽不是反复地释放并重建。(简⾔之,就是cache)

如果程序创建多个runtime,你需要清楚哪⼀个runtime和哪⼀个context相关联。想了解更多,请参考使⽤和来把应⽤程序相关的数据与context关联起来。Initializing built-in and global JS objects

如果想了解SpiderMonkey所有内置的对象,可以参考.

应⽤程序提供的全局对象在很⼤程度上决定了脚本可以做哪些事情。⽐如,FireFox浏览器使⽤它⾃⼰的全局对象windows。你可以更改⾃已应⽤的全局对象,使⽤Creating and initializing custom objects

除了引擎内置的对象,你还可以创建、初始化和使⽤你⾃⼰的js对象。特别是在你想使⽤js引擎来⾃动化你的程序。定制的js对象可以提供了⼀些直接的服务,或者说它们充当了你的脚本和程序的接⼝。⽐如,⼀个定制的对象可以提供你程序中所有的⽹络访问功能,也可以充当你的程序与数据库交互的⼀个中介者。或者可以把你的程序⾥⾯原来采⽤c或者c++编写的功能代码映射成⼀种js的⾯向对象形式。这种内置的对象其实就是你的脚本与程序核⼼交流的桥梁。你可以从脚本⾥⾯传值给程序,也可以从程序⾥⾯返回值给脚本。

⽬前有两种⽅法让js引擎创建定制的对象:

编写⼀个js脚本来创建对象,它的属性、构造器、函数,然后在运⾏时把这个对象传给js引擎。

在你的程序中嵌⼊⼀些代码,这些代码定义了js对象的属性和⽅法,然后调引擎基于这些代码去创建相应的js对象。这种⽅法的好处是,你的程序包含了本地⽅法可以直接操作本地对象。Creating an object from a script

从脚本中创建对象,原因之⼀就是你只想让这个定制对象和脚本的⽣命周期⼀致。如果你想创建⼀些对象可以在多个脚本中使⽤,就应该把这个对象嵌⼊到应⽤程序中,⽽不是写在脚本⾥⾯。注意:你也可以使⽤脚本来创建持久的对象。使⽤脚本来创建定制对象:

定义和设计该对象。它的⽤途是什么?它有哪些数据成员?它有哪些⽅法?它需要⼀个构造函数吗?

编写js代码来定义并创建对象。⽐如,function myfunc(){var x = newObject()}}。注意,使⽤js脚本编写的js对象是在js引擎context之外的。如果想了解更多有关对象脚本化的内容,可以参考Client-SideJavaScript Guide 和 Server-side JavaScript Guide.嵌⼊⼀个合适的js引擎到你的应⽤程序中,然后编译并执⾏这些脚本。你有两个选择:1)每次调⽤⼀个函数都使⽤JS_EvaluateScript和

JS_EvaluateUCScript来编译来执⾏js脚本。2)使⽤JS_CompileScript或者JS_CompileUCScript来编译脚本,然后可以使⽤JS_ExecuteScript来重复使⽤之。这个”UC”版本可以⽀持unicode脚本。你使⽤脚本创建的对象,它的⽣命周期与脚本本⾝是⼀致的。你也可以创建⼀些对象,它们在脚本执⾏完成之后,还可以存在。⼀般情况是,当脚本运⾏完,这些被脚本创建的对象就被销毁了。在许多情况下⾯,这正是你的应⽤程序所期待的⾏为。但是,也有⼀些特殊的情况,你可能需要让某些对象可以⼤多个脚本之间共享。Custom objects

⼀个应⽤程序可以不⽤JSClass来创建定制对象:

在c/c++端使⽤你的对象的setter/getter和⽅法。然后为每⼀个setter or getter编写⼀个。为每⼀个⽅法编写JSNative或者JSFastNative⽅法。为你的对象的所有的属性,包括gettter and settter声明数组。为你的定制对象的所有的⽅法声明数组。调⽤, or 来创建对象。调⽤来定义对象属性调⽤来定义对象的⽅法.

也可以⽤来创建对象的属性。但是它创建的属性没有getter和setter。这些属性只是普通的Javascript属性。Providing private data for objects

就像context⼀样,你可以把很多数据与对象进⾏关联,⽽不需要显式把这些数据当作对象本⾝的属性。调⽤可以为对象建⽴⼀个指向私有数据的指针。然后再调⽤来访问这些私有数据。你的应⽤程序本⾝负责创建和管理这些可选的私有数据。

想要为你的私有对象创建私有数据,可以按如下⽅法进⾏:把私有数据和⼀个普通的c指针关联起来。调⽤,指定对象要与哪个私有数据建⽴联接。⽐如:

1 JS_SetPrivate(cx, obj, pdata);

为了在后续可以访问这些数据,调⽤,然后把对象作为参数传进来。这个函数会返回此对象的私有数据指针。1pdata = JS_GetPrivate(cx, obj);

后记:后⾯的部分⽐较⾼级,讲的是unicode,安全性,引擎debug和引擎性能调试的内容,如果想继续了解的读者可以参考下⾯的链接。Reference:

因篇幅问题不能全部显示,请点此查看更多更全内容

Top