2023年11月29日发(作者:)
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对象,以及XMLHTTPRequest.但是,在Mozilla⾥⾯所有的特性都是由另外的组件提供,⽽不是
SpiderMonkey引擎本⾝。SpiderMonkey引擎本⾝只提供javascript核⼼数据类型,⽐如number,string,array,object等。还有⼀些常⽤的⽅法,⽐如。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 对象作为第⼀个参数。就像
在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应⽤程序。它包含了我们之前介绍的所有知识点:
1
2
3
4
5
6
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_MEMBERS
20};
21/* The error reporter callback. */
22void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
23 fprintf(stderr, "%s:%u:%sn",
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}
61
62
63
64
65
66
每⼀个JSNative有⼀样的签名,完全可以忽视Javascript那边所传递的函数参数。
传递给javascript函数的参数由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。
1
2
3
4
5
6
7
static JSFunctionSpec myjs_global_functions[] = {
JS_FS("rand", myjs_rand, 0, 0),
JS_FS("srand", myjs_srand, 0, 0),
JS_FS("system", myjs_system, 1, 0),
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_OBJECT
2JSVAL_IS_NUMBER
3JSVAL_IS_INT
4JSVAL_IS_DOUBLE
5JSVAL_IS_STRING
6JSVAL_IS_BOOLEAN
7JSVAL_IS_NULL
8JSVAL_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槽,可以使⽤
.
如果⼀个定制的对象需要把某些值存储在内存中,只需要把这些值当作此对象的属性存储起来即可。只要这个对象是可以被引⽤的,那么它的属性就是可以被访问到的。如果这些值不⼀定要让js代码访
问,可以使⽤保留槽。 也可以使⽤⼀个,然后把值存储到中。
如果⼀个函数创建新的对象、字符串和数字,它可以使⽤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", rc);
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
1
2
3
4
5
/* Statically initialize a class to make "one-off" objects. */
6
JSClass my_class = {
7
"MyClass",
8
/* All of these can be replaced with the corresponding JS_*Stub
9
function pointers. */
10
my_addProperty, my_delProperty, my_getProperty, my_setProperty,
11
my_enumerate, my_resolve, my_convert, my_finalize
12
};
13
JSObject *obj;
14
/*
15 * Define an object named in the global scope that can be enumerated by
16 * for/in loops. The parent object is passed as the second argument, as
17 * with all other API calls that take an object/name pair. The prototype
18 * passed in is null, so the default object prototype will be used.
19 */
20obj = JS_DefineObject(cx, globalObj, "myObject", &my_class, NULL,
21 JSPROP_ENUMERATE);
22/*
23 * Define a bunch of properties with a JSPropertySpec array statically
24 * 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 used
26 * 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_RDONLY
30};
31static JSPropertySpec my_props[] = {
32 {"color", MY_COLOR, JSPROP_ENUMERATE},
33 {"height", MY_HEIGHT, JSPROP_ENUMERATE},
34 {"width", MY_WIDTH, JSPROP_ENUMERATE},
35 {"funny", MY_FUNNY, JSPROP_ENUMERATE},
36 {"array", MY_ARRAY, JSPROP_ENUMERATE},
37 {"rdonly", MY_RDONLY, JSPROP_READONLY},
38 {0}
39};
40JS_DefineProperties(cx, obj, my_props);
41/*
42 * Given the above definitions and call to JS_DefineProperties, obj will
43 * need this sort of "getter" method in its class (my_class, above). See
44 * the example for the "It" class
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}
61
62
63
Definning classes
这⼀部分内容通过使⽤JSAPI来定义构造函数,原型对象,原型对象的属性,以及构造函数的属性。
初始化⼀个类,可以通过定义构造函数,原型和⼀些预定义的实例属性和类属性。类属性和java中的静态属性有点相似。它们都被定义在对象的构造函数作⽤域内,这样icProp就可以和
new MyClass()⼀起被js端所使⽤了。
接收很多参数,但是,最后4个参数是可以传递空的。如果你不想让定义的类有相应的属性的话。
注意,你不需要去调⽤JS_InitClass来创建⼀个类的实例,这是⼀个先有鸡还是先有蛋的问题。你只需要调⽤JS_InitClass,这样脚本在执⾏new操作的时候就可以找到相应的构造函数了。这样js端就可
以通过运⾏时从对象的prototype对象(ypea)或者继承的对象中访问相应的属性。⼀般来讲,如果你想让多个实例共享同样的⾏为,可以使⽤JS_InitClass.
1
2
⽬前有两种⽅法让js引擎创建定制的对象:
编写⼀个js脚本来创建对象,它的属性、构造器、函数,然后在运⾏时把这个对象传给js引擎。
在你的程序中嵌⼊⼀些代码,这些代码定义了js对象的属性和⽅法,然后调引擎基于这些代码去创建相应的js对象。这种⽅法的好处是,你的程序包含了本地⽅法可以直接操作本地对象。
Creating an object from a script
从脚本中创建对象,原因之⼀就是你只想让这个定制对象和脚本的⽣命周期⼀致。如果你想创建⼀些对象可以在多个脚本中使⽤,就应该把这个对象嵌⼊到应⽤程序中,⽽不是写在脚本⾥⾯。
注意:你也可以使⽤脚本来创建持久的对象。
使⽤脚本来创建定制对象:
定义和设计该对象。它的⽤途是什么?它有哪些数据成员?它有哪些⽅法?它需要⼀个构造函数吗?
编写js代码来定义并创建对象。⽐如,function myfunc(){var x = newObject()}}。注意,使⽤js脚本编写的js对象是在js引擎context之外的。如果想了解更多有关对象脚本化的内容,可以参考Client-Side
JavaScript 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:


发布评论