大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE稳定放心使用
原文地址:https://developer.mozilla.org/en-US/docs/SpiderMonkey/JSAPI_User_Guide
文章为郭胜龙所写,转载说明出处
本文是mozilla官网上关于spiderMonder的一篇用户指南,正好要用,顺带翻译了一下:
JSAPI 用户指南
全局对象:全局对象包括所有可被JS代码用的类,方法,变量。例如当js代码进行了类似window.open的操作,它会获取一个全局的属性,在这个例子中是window。JSAPI对可见的全局属性脚本有全权的控制。该应用程序通过创建一个对象,并用标准的JavaScript类类似Array和Object填充它来开始,然后它再把应用程序希望提供的自定义类、函数和变量(类似window);每次应用程序运行JS脚本(例如使用JS_EvaluateScript),它会提供一个全局的对象供这个脚本使用。当脚本运行时它会创建自己的全局的函数和变量。所有这些函数和变量以属性的形式储存在全局对象中。
一个简单的例子:
3个关键元素中每个都需要一些JSAPI调用:
runtime:使用JS_NewRuntime来创建并使用JS_DestroyRuntime来销毁。当你的应用不使用了,使用JS_ShutDown来释放所有缓存资源。(每次结束都调用是个好习惯!!!)
context:使用 JS_NewContext
and JS_DestroyContext。为了保证ECMAScript 标准的一致性,应用也需要使用JS_SetOptions来开启JSOPTION_VAROBJFIX。为了获得JS的新功能,应用可能会使用JS_SetVersion。错误报告也是每个context都需要并且通过启用JS_SetErrorReporter来使用。
golbal object:你需要一个设置了JSCLASS_GLOBAL_FLAGS选项的JSClass。下面的例子定义了一个非常简单的JSClass(叫global_class)没有自己的属性和 方法。使用JS_NewGlobalObject来创建一个全局对象。使用JS_InitStandardClasses来用标准的JS全局变量填充它。
JSAPI一般设计应用程序的规模会有多个线程,多个contexts和多个全局对象。它是细粒度的API,支持各种部分的组合,给应用对SpiderMonkey行为的绝对掌控。
下面是一个小例子,包含上面讨论的所有东西:
#include "jsapi.h" /* The class of the global object. */ static JSClass global_class = {
"global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, JSCLASS_NO_OPTIONAL_MEMBERS };
/* The error reporter callback. */ void reportError(JSContext *cx, const char *message, JSErrorReport *report) {
fprintf(stderr, "%s:%u:%s\n", report->filename ? report->filename : "[no filename]", (unsigned int) report->lineno, message); }
int run(JSContext *cx) {
/* Enter a request before running anything in the context */ JSAutoRequest ar(cx); /* Create the global object in a new compartment. */ JSObject *global = JS_NewGlobalObject(cx, &global_class, NULL); if (global == NULL) return 1; /* Set the context's global */ JSAutoCompartment ac(cx, global); JS_SetGlobalObject(cx, global); /* Populate the global object with the standard globals, like Object and Array. */ if (!JS_InitStandardClasses(cx, global)) return 1; /* Your application code here. This may include JSAPI calls to create your own custom JS objects and run scripts. */ return 0; }
int main(int argc, const char *argv[]) {
/* Initialize the JS engine -- new/required as of SpiderMonkey 31. */ if (!JS_Init()) return 1; /* Create a JS runtime. */ JSRuntime *rt = JS_NewRuntime(8L * 1024L * 1024L, JS_NO_HELPER_THREADS); if (rt == NULL) return 1; /* Create a context. */ JSContext *cx = JS_NewContext(rt, 8192); if (cx == NULL) return 1; JS_SetOptions(cx, JSOPTION_VAROBJFIX); JS_SetErrorReporter(cx, reportError); int status = run(cx); JS_DestroyContext(cx); JS_DestroyRuntime(rt); /* Shut down the JS engine. */ JS_ShutDown(); return status; }
每个JSNative有相同的签名,忽视期望js传回的参数。
传给函数的js参数在argc和vp.argc中给出,告诉它回调传了几个参数,JS_ARGV(cx, vp)返回这些参数的一个数组。参数并没有原生的C++类型类似int 和 float;他们是jsval类型。原生的函数使用JS_ConvertArguments来转换参数为C++类型并将他们储存在本地变量中。本地函数使用JS_SET_RVAL(cx, vp, val)来存储它的js返回值。
成功的前提是JSNative必须调用JS_SET_RVAL并且返回JS_TRUE。传给JS_SET_RVAL的值在js回调中返回。
失败的话JSNative调用一个错误报告函数,在这个例子中为JS_ReportError,并且返回JS_FALSE。这样会使异常抛出。调用可以被Try/catch捕捉到。
为了让原生的函数可以被js调用,声明一个JSFunctionSpec表来描述这个函数,然后调用JS_DefineFunctions
.
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
};
...
if (!JS_DefineFunctions(cx, global, myjs_global_functions))
return JS_FALSE;
...
一旦函数在golbal中定义了,任何使用golbal的脚本可以调用他们,就像任何的web网页可以调用alert一样。在我们创建的环境中,“hello world”应该这样写:
system("echo hello world");
JSAPI概念
这个版块主要目的是提出JSAPI的重大缺陷,要用spiderMonkey开发必须要读完所有的额版块。
JavaScript values
主要的文章:JS::value
js是一个动态类型的语言:变量和属性没有在编译的时候修正的。那么类似c和C++这些所有变量都有类型的语言如何与js交互呢?JSAPI提供了一个数据类型,JS::Value(也有一个弃用的jsval类型),可以包含任何js值得类型。一个JS::value可以是一个数字、一个字符串和一个bool型的值,一个对象的引用(类似Object
, Array
, Date
, or Function
),或者一个特殊类型例如:null或者undefined。
对于整形和bool的值,jsval包含值,在其他情况下,jsval是一个object, string, or number指针。
jsval包含成员函数类检测js数据的类型。他们是是isObject()
,isNumber()
, isInt32()
, isDouble()
, isString()
, isBoolean()
, isNull()
, and isUndefined()
.
如果jsval包含一个JSObject,double,或者JSString,你可以把它通过成员函数toObject()
, toDouble()
, and toString()
转换成它的基本数据类型。你的应用或者JSAPI函数需要特殊数据类型的值和参数而不是jsval时会很有用。类似的,你可以创建JS::Value绑定一个JSObject、double,JSString指针到javal对象使用JS::ObjectValue(JSObject&)
,JS::DoubleValue(double)
或者JS::StringValue(JSString*).
垃圾回收
当运行时,js 代码隐式分配对象的内存,strings,变量等等。垃圾回收是当有片段的内存不可到达时通过js引擎检查的过程,就是说,他们不能再次使用,并且回收。
垃圾回收有两个重要的影响。第一,应用必须非常确定它需要的任何值都是GC可以到达的。垃圾回收机制对这些非常乐意处理。如果你们有告诉JSAPI你还会用它那么任何你留下的对象将被销毁。第二,应用应该减少垃圾收集的性能影响。
保持对象存在
如果你的JSAPI应用崩溃了,一般都是Gc相关的错误。应用必须确保垃圾回收器可以到达所有仍在使用的对象,数字,字符串。否则,GC将释放被这些值占用的内存,下次使用的时候导致程序崩溃。
下面有几种方法来保证值是GC-reachable。
1、如果你只是需要JSNative调用时限中保持可达,那么将其存储在*rval或者argv数组的一个元素中。储存在这些地方的值总是可达的。要获得更多额外的argx插槽,可以使用 JSFunctionSpec.extra
.
2、如果一个自定义的对象需要确定的值保持在内存中,只要将值储存在对象的属性中。对象和属性都将保持可达。如果这些值不需要用js调用,可以使用reserved slots来替代。或者将值储存在私有数据中并实现JSClass.mark。
3、如果一个函数创建了新的对象,string或者数字,它可以使用JS_EnterLocalRootScope和JS_LeaveLocalRootScope来保持这些值在函数的生命周期中存在。
4、要让值永久有效,将他储存在GC root中。
但不管如何,GC bug还是会发生。以下这两个函数(只在DEBUG环境下有效),对调试GC相关的崩溃特为的有用:
1、使用JS_SetGCZeal来开启额外垃圾回收。GC zeal通常会导致GC相关崩溃发生。只用于开发和调试,因为额外的垃圾回收是的js非常慢。
2、使用JS_DumpHeap来转存SpiderMonkey堆或者特殊的感兴趣部分。
GC的性能
过于频繁的垃圾回收会导致性能问题。一些应用可以通过增加JSRuntime的初始化大小来减少垃圾回收的次数。
但它影响到用户时大概最好的技术是在空闲的时间中进行垃圾回收。默认的,js引擎在没有别的选择只有发展过程。这个意味着垃圾回收一般在内存密集型代码运行时发生。应用可以随时通过调用JS_GC
or JS_MaybeGC
触发垃圾回收。
错误和异常
检查JSAPI函数返回值这一步骤的重要性是没的说的。差不多每个带有JSContext*参数的JSAPI函数都可以出错。系统可能会运行完内存。这里可能会有一个脚本的语法错误。或者脚本显式的抛出一个异常。
js语言和C++都有异常,但是他们不是相同的东西。SpiderMonkey不使用C++异常来处理东西。JSAPI函数从不抛出C++异常,当SpiderMonkey调用一个应用的回调时,回调必须不抛出C++异常。
抛出和捕捉异常
我们已经看了如何从一个JSNative函数中抛出异常。只需要简单调用JS_ReportError,加上
printf
的格式参数,并且返回JS_FALSE。
rc = system(cmd);
if (rc != 0) {
/* Throw a JavaScript exception. */
JS_ReportError(cx, "Command failed with exit code %d", rc);
return JS_FALSE;
}
这很像JS语句
throw new Error(“Command failed with exit code ” + rc);。同样注意调用JS_ReportError
不会导致C++异常抛出。它只会创建一个新的js错误对象并且在context中保存作为当前的挂起异常。应用同样返回JS_FALSE。
一旦C++函数返回JS_FALSE,js引擎开始展开js栈,寻找catch或者finally代码块来执行。
错误报告
自己要做的事自定义错误报告并且把我什么时候报告。
自动处理未捕获的异常
更多的例子
定义对象和属性
/* 静态初始化一个类来创建“一次性”对象. */
JSClass my_class = {
"MyClass",
/* 所有这些都可以用JS_*Stub函数指针来取代. */
my_addProperty, my_delProperty, my_getProperty, my_setProperty,
my_enumerate, my_resolve, my_convert, my_finalize
};
JSObject *obj;
/*
* 定义一个全局范围内的对象可以在for/in循环中枚举。
* 第二个参数为父对象,三、四参数和普通API调用一样是名字+类。
* 原型传递是空,所以将使用默认对象原型
*/
obj = JS_DefineObject(cx, globalObj, "myObject", &my_class, NULL,
JSPROP_ENUMERATE);
/*
* 用JSPropertySpec静态数组初始化定义了一堆属性,以{0}结束。除了名字,每个属性还有
* 一个“tiny”定义(例如MY_COLOR) 可以用在switch语句中(例如下文中的my_getProperty函数)
*/
enum my_tinyid {
MY_COLOR, MY_HEIGHT, MY_WIDTH, MY_FUNNY, MY_ARRAY, MY_RDONLY
};
static JSPropertySpec my_props[] = {
{"color", MY_COLOR, JSPROP_ENUMERATE},
{"height", MY_HEIGHT, JSPROP_ENUMERATE},
{"width", MY_WIDTH, JSPROP_ENUMERATE},
{"funny", MY_FUNNY, JSPROP_ENUMERATE},
{"array", MY_ARRAY, JSPROP_ENUMERATE},
{"rdonly", MY_RDONLY, JSPROP_READONLY},
{0}
};
JS_DefineProperties(cx, obj, my_props);
/*
* 鉴于上述定义和JS_DefineProperties的调用,obj将需要类似这种的“getter”方法在类中。
*/
static JSBool
my_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
if (JSVAL_IS_INT(id)) {
switch (JSVAL_TO_INT(id)) {
case MY_COLOR: *vp = . . .; break;
case MY_HEIGHT: *vp = . . .; break;
case MY_WIDTH: *vp = . . .; break;
case MY_FUNNY: *vp = . . .; break;
case MY_ARRAY: *vp = . . .; break;
case MY_RDONLY: *vp = . . .; break;
}
}
return JS_TRUE;
}
定义类
通过定义一个构造函数可以将上述的API元素都集合起来:一个原型对象、原型和构造函数的属性都放到一个API调用中。
通过定义类的构造函数、原型、类属性初始化一个类,后者类比于java中的static。他们在构造函数对象的域中定义,所以只有new出来的额class才能使用MyClass.myStaticProp。
JS_InitClass有许多参数,但是你可以对最后四个参数传递NULL如果他们没有这些属性或者方法。
注意你不需要使用JS_InitClass来创建class的新实例,否则创建全局对象时会出现鸡与蛋的问题,但是当你需要一个构造函数通过new来调用时你应该调用JS_InitClass,以新的属性来延伸原型对象(MyClass.Prototype)。总的来说,如果你希望支持多个实例共享行为,使用JS_InitClass。
protoObj = JS_InitClass(cx, globalObj, NULL, &my_class,
/* native constructor function and min arg count */
MyClass, 0,
/* prototype object properties and methods -- these
will be "inherited" by all instances through
delegation up the instance's prototype link. */
my_props, my_methods,
/* class constructor properties and methods */
my_static_props, my_static_methods);
运行脚本
/* 这里指出源文件的地址. */
char *filename;
uintN lineno;
/*
* The return value comes back here -- if it could be a GC thing, you must
* add it to the GC's "root set" with JS_AddRoot(cx, &thing) where thing
* is a JSString *, JSObject *, or jsdouble *, and remove the root before
* rval goes out of scope, or when rval is no longer needed.
*/
jsval rval;
JSBool ok;
/*
* Some example source in a C string. Larger, non-null-terminated buffers
* can be used, if you pass the buffer length to JS_EvaluateScript.
*/
char *source = "x * f(y)";
/**
*编译并且执行
*/
ok = JS_EvaluateScript(cx, globalObj, source, strlen(source),
filename, lineno, &rval);
if (ok) {
/* Should get a number back from the example source. */
jsdouble d;
ok = JS_ValueToNumber(cx, rval, &d);
. . .
}
调用函数
/* Call a global function named "foo" that takes no arguments. */
ok = JS_CallFunctionName(cx, globalObj, "foo", 0, 0, &rval);
jsval argv[2];
/* Call a function in obj's scope named "method", passing two arguments. */
argv[0] = . . .;
argv[1] = . . .;
ok = JS_CallFunctionName(cx, obj, "method", 2, argv, &rval);
JSContext
因为维护一个context需要一定的开销,因此应用应该:
1、同一时间只创建需要的context
2、一直保存他们比销毁重建要好得多
如果你的应用创建了多个runtimes,应用可能需要知道那个context对应那个runtime,在这种情况下,使用JS_GetRuntime。
使用JS_SetContextPrivate和JS_GetContextPrivate来和context关联应用特定的数据。
初始化内置对象和全局JS对象
对于SpiderMonkey提供的内置对象的完整的列表,参见S_InitStandardClasses
.
应用提供给脚本的全局对象决定了脚本能做什么。例如,火狐浏览器使用自己的全局对象:windows。调用JS_SetGlobalObject
.来改变全局对象。
创建并初始化自定义对象
除了使用引擎的内置对象,你还可以创建,初始化和使用自己的JS对象。你可以使用JS引擎自动化执行你的应用。自定义JS对象可以提供直接的程序服务,或者他们可以作为你的程序的服务接口。例如,一个自定义的JS对象,他提供的直接服务可能是处理一个应用程序的所有网络访问,或者作为数据库服务的中间件。或者一个数据镜像和函数都已经存在于应用中为C代码提供面向对象的接口或者严格来讲,面向对象本身。这些自定义对象作为应用程序本身的接口,将值从应用程序传递给用户,接受并且在返回给应用之前处理用户输入。这样的一个对象可能被用来提供一个访问基本功能的接口。
有两种方法来创建JS引擎可以使用的自定义对象:
1、编写一个JS脚本创建一个对象,脚本中有他的属性、方法和构造函数,然后再运行时把脚本传递给JS引擎。
2、在你的应用程序中嵌入代码,定义了对象的属性和方法,调用引擎来初始化一个新的对象,然后再通过额外的引擎调用设置对象的属性。这种方法的优点是,你的应用程序可以包含直接操作对象嵌入的本地方法。
在这两种情况下,如果你创建了一个对象,然后希望他一直可谓别的脚本使用的话,你必须要通过调用JS_AddRoot or JS_AddNamedRoot来固定对象。使用这些函数来确保JS引擎会保持对对象的跟踪并且在垃圾回收是清除他们,如果合适的话。、
通过脚本创建对象
通过脚本创建对象的其中一个原因在于当你只需要一个对象,它只在脚本运行过程中存在。要创建一个之持久保存于脚本调用的对象,你可以用嵌入对象代码来替代。
注意:你也可以通过脚本来创建持久保存的对象。
使用脚本创建自定义对象:
1、定义并且设定对象的细则。
2、编写定义脚本代码并且创建对象。例如: function myfun(){ var x = newObject(); . . . } 注意: 在你的应用中嵌入相应的JS引擎调用来编译并且运行脚本。你有两个选择:1、编译并且执行脚本使用一个简单的JS_EvaluateScript调用,JS_EvaluateUCScript 。2、先使用JS_CompileScript或者 JS_CompileUCScript编译脚本,然后使用JS_ExecuteScript各自执行。“UC”版本为为脚本提供Unicode编码支持。
使用脚本创建的一个对象只能在脚本的生命周期里可使用,或者可以创建持久型脚本。通常情况下,一旦脚本执行完成了,他的独显被销毁了。在很多情况下,这个行为正是你所需要的。但在有些时候,你可能想让对象在你的应用生命周期里持久化。在这些情况下你需要直接在你的应用程序中嵌入你的对象创建代码,或者你需要直接把对象关联到全局对象上,这样只要全局对象存在,它就存在。
自定义对象
应用程序可以在不需要JSClass支持下创建对象。
1、用C或C++实现自定义对象的getter、setter和方法。为每个getter和setter编写一个JSPropertyOp。为每个方法编写一个JSNative或者JSFastNative。
2、声明一个包含自定义对象的属性规格的JSFunctionSpec数组,包括getter和 setter。
3、声明一个包含自定义对象的方法规格的JSFunctionSpec数组。
4、调用
JS_NewObject
, JS_ConstructObject
, or JS_DefineObject来创建对象。
5、调用 JS_DefineProperties来定义对象的属性。
6、调用JS_DefineFunctions来定义对象的方法。
JS_SetProperty也可以用在创建对象的属性。这个属性创建是没有getter和setter的,是普通的js属性。
为对象提供私有数据
类似contexts,你可以与一个对象关联大量的数据而不需要储存数据到对象本身。调用JS_SetPrivate
指定要建立私有数据的对象,并指定指向数据的指针。
例如:
JS_SetPrivate(cx, obj, pdata);
在之后要获取数据,可以调用
JS_GetPrivate,并且作为一个参数传递对象。这个函数返回对象私有数据的指针:
pdata = JS_GetPrivate(cx, obj);
编译脚本
允许脚本最简单的方法就是使用JS_EvaluateScript,它将编译和运行绑定到一块去了。
但是有时候一个应用需要多次运行一个脚本。在这种情况下,编译一次执行多次要快一点。
JSAPI提供JSScript类型来代表一个编译好的脚本。JSScript的生命周期如下:
应用程序使用JS_CompileScript
, JS_CompileUTF8File
, orJS_CompileFileHandle
编译一些js代码。这些函数返回一个新的JSScript的指针。
应用调用JS_ExecuteScript
(or JS_ExecuteScriptPart
) 任意次数。在不同的contexts和不同的global对象中使用JSScript是安全的,但必须保证在创建时的runtime和线程中。
下面是一个例子:
/*
* Compile a script and execute it repeatedly until an
* error occurs. (If this ever returns, it returns false.
* If there's no error it just keeps going.)
*/
JSBool compileAndRepeat(JSContext *cx, const char *filename)
{
JSScript *script;
script = JS_CompileUTF8File(cx, JS_GetGlobalObject(cx), filename);
if (script == NULL)
return JS_FALSE; /* compilation error */
for (;;) {
jsval result;
if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result))
break;
JS_MaybeGC(cx);
}
return JS_FALSE;
}
已编译脚本的生命周期是和js对象的生命周期关联的,当脚本不再可达的时候垃圾回收器销毁脚本。JSAPI通过
JS_NewScriptObject函数来提供这一特点,使用这一特点的脚本的生命周期是这样的:
1、应用编译了一些js代码
2、为了保护已编译的脚本免受垃圾回收器的误删,应用通过调用JS_NewScriptObject创建了一个已编译对象并且使用JS_SetProperty
, JS_SetReservedSlot
, JS_AddRoot或其他函数来使得这个对象是垃圾回收可达的。
3、应用执行任意次数的已编译脚本。
4、在应用的执行中,当已编译脚本终于再也不需要了,已编译脚本将变成不可达的。
5、最终垃圾回收器回收不可达的脚本和它的其他部分。
下面是一个例子示范这个技术–注意在这个例子中不够复杂去保证JS_NewScriptObject的使用,上面的例子更加直接。
/*
* Compile a script and execute it repeatedly until an
* error occurs. (If this ever returns, it returns false.
* If there's no error it just keeps going.)
*/
JSBool compileAndRepeat(JSContext *cx, const char *filename)
{
JSScript *script;
JSObject *scriptObj;
script = JS_CompileUTF8File(cx, JS_GetGlobalObject(cx), filename);
if (script == NULL)
return JS_FALSE; /* compilation error */
scriptObj = JS_NewScriptObject(cx, script);
if (scriptObj == NULL) {
JS_DestroyScript(cx, script);
return JS_FALSE;
}
if (!JS_AddNamedObjectRoot(cx, &scriptObj, "compileAndRepeat script object"))
return JS_FALSE;
for (;;) {
jsval result;
if (!JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &result))
break;
JS_MaybeGC(cx);
}
JS_RemoveObjectRoot(cx, &scriptObj); /* scriptObj becomes unreachable
and will eventually be collected. */
return JS_FALSE;
}
跟踪分析
JSAPI提供了可以便捷的实现js跟踪和分析的功能。
函数跟踪
如果你配置了启用js调用跟踪,你可以使用JS_SetFunctionCallback()来建立一个C函数,它会在js函数将要调用或者执行完成之后调用:
void funcTransition(const JSFunction *func, const JSScript *scr, const JSContext *const_cx, JSBool entering) {
JSContext *cx = const_cast<JSContext*>(const_cx); JSString *name = JS_GetFunctionId((JSFunction*)func); const char *entExit; const char *nameStr; /* build a C string for the function's name */ if (!name) {
nameStr = "Unnamed function"; } else {
nameStr = JS_EncodeString(cx, name); } /* build a string for whether we're entering or exiting */ if (entering) {
entExit = "Entering"; } else {
entExit = "Exiting"; } /* output information about the trace */ printf("%s JavaScript function: %s at time: %ld", entExit, nameStr, clock()); } void enableTracing(JSContext *cx) {
JS_SetFunctionCallback(cx, funcTransition); } void disableTracing(JSContext *cx) {
JS_SetFunctionCallback(cx, NULL); }
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/181049.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...