SpiderMonkey 入门

SpiderMonkey 入门原文地址 https developer mozilla org en US docs SpiderMonkey JSAPI User Guide 文章为郭胜龙所写 转载说明出处本文是 mozilla 官网上关于 spiderMonder 的一篇用户指南 正好要用 顺带翻译了一下 JSAPI 用户指南全局对象 全局对象包括所有可被 JS 代码用的类 方法 变量 例如当 js 代码

原文地址: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型的值,一个对象的引用(类似ObjectArrayDate, 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_NewObjectJS_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_CompileScriptJS_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_SetPropertyJS_SetReservedSlotJS_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); }




版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/212807.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午7:13
下一篇 2026年3月18日 下午7:13


相关推荐

  • IDEA自动导包(详细教程)

    IDEA自动导包(详细教程)1 打开 IDEA 找到设置进入一下最后点击应用成功 妈妈在也不用担心我手动导包了

    2026年3月18日
    3
  • 最大匹配、最小顶点覆盖、最大独立集、最小路径覆盖(转)

    最大匹配、最小顶点覆盖、最大独立集、最小路径覆盖(转)

    2022年2月4日
    54
  • Spring batch批量处理框架最佳实践

    Spring batch批量处理框架最佳实践springbatch精选,一文吃透springbatch批量处理框架前言碎语批处理是企业级业务系统不可或缺的一部分,springbatch是一个轻量级的综合性批处理框架,可用于开发企业信息系统中那些至关重要的数据批量处理业务.SpringBatch基于POJO和Spring框架,相当容易上手使用,让开发者很容易地访问和利用企业级服务.springbatch具有高可扩展性的框架…

    2022年5月23日
    37
  • HDMI管脚定义

    HDMI管脚定义1 DDC SCL 和 DDC SDA 用处 DDC 显示数据通道 主要用于 HDMI 源端设备 Source 与接收端设备 Sink 之间进行 EDID 数据及 HDCP 密钥的交流 通过 EDID 交流 源端设备可以了解到接收端设备音视频的接收能力 通过 HDCPKey 的交流 可以实时的进行数据流的内容保护认证 从而达到数据内容保护的目的 2 DDC 的电路 DDC 的电路方式与 I2C 电路相同 因此

    2026年3月18日
    2
  • C#编写OPC客户端读取OPC服务器的数据(最高效简洁版)「建议收藏」

    C#编写OPC客户端读取OPC服务器的数据(最高效简洁版)「建议收藏」想要了解更多,可以添加扣扣群143440558,免费交流,免费下载以上文件,免费了解更多编写OPC客户端,网上的资料一般是一上来就要求找OPCDAAuto.dll,其实我想说,用VS,那都是多此一举,当然,如果你是在需要,我也可以提供给您最新版OPCDAAuto.dll(v2.2.5.30)(https://download.csdn.net/download/wanghuaihu/11…

    2022年6月20日
    103
  • 阿里笔试题目「建议收藏」

    阿里笔试题目「建议收藏」题目描述一个淘宝的订单中包含n(10&gt;=n&gt;=1)种商品A1,A2,…,An,每种商品数量分别为a1,a2,…,an个,记做{a1,a2,…,an}(ak&gt;0)。订单在仓库生产过程中,仓库为了提升作业效率,会提前对热门组合商品进行预包装。假设这n个商品有m(9&gt;=m&gt;=1)个商品组合,每个组合bomk包含A1,A2,…,An的数量分别为{b1,b2,…,bn}(bk&…

    2022年5月10日
    42

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号