7.6 CLR/Javascript(SpiderMonkey)
Limax提供一个clrjs项目,粘合C#与Javascript,支持C#与Javascript代码之间的互操作,用于实现C#脚本模式框架下的JavaScriptHandle。这一章介绍clrjs提供的功能。
-
7.6.1 编程接口
-
limax.script.Js
创建该对象也就创建了一个javascript虚拟机,使用该对象即可与javascript虚拟机交互。
构造函数:
-
Js(uint maxbytes)
maxbytes决定了javascript虚拟机使用的最大内存字节数。
-
Js()
调用上一个构造函数,默认maxbytes = 8388608
成员函数:
-
object eval(string code)
执行code表示的代码chunk,返回结果。
-
object eval(string codepattern, params object[] parameters)
用变长参数列表填充codepattern中的占位符<0>,<1>......
,然后执行chunk,返回结果。占位符通过与之对应创建的上下文变量进行关联,在模板代码中应该当作变量名使用,而不能用引号括起来,否则将替换结果将是一个上下文变量名串。 返回的结果可能是任何类型,特别的可能返回JsObject,JsArray,JsFunction类型。
-
Js name(string chunkname)
为后续eval命名, 如果运行时错误发生在被命名的eval的代码块中, 错误信息中会前缀该名字, 便于调试. 该名字内部使用UTF8编码, 建议不要使用非ASCII字符.
异常:
-
ScriptException
见异常规范一节。
-
ThreadContextException
见线程安全一节。
-
-
limax.script.JsObject
无法转换为C#类型的javascript对象,哪些无法转换在类型映射一节介绍,该对象实现了System.Collections.IDictionary接口,便于在C#代码中操纵javascript对象的属性。
-
limax.script.JsArray
派生自JsObject,对应了javascript中的Array,实现了IList接口,便于在C#代码中操纵javascript的Array对象。
-
limax.script.JsFunction
一个变长参数委托,包装了一个派生自JsObject,关联到javascript虚拟机中的Function上的对象。在该委托上进行调用,相当于调用了虚拟中对应的Function。
注意,通过limax.script.Js创建的对象,在之后的文字中命名为javascript操作对象,区别于JsObject。
-
-
7.6.2 C#与Javascript的互操作举例
首先定义Js js = new Js();
-
C#操纵Javascript
C#操纵Javascript,通过eval方法调用实现。
-
hello world
js.eval("print('hello world')"); js.eval("print(<0>)", "hello world"); Console.WriteLine(js.eval("'hello world'")); Console.WriteLine(js.eval("<0>", "hello world"));
输出:
hello world
hello world
hello world
hello world
其中, 第一行代码无需解释, 第二行代码说明了占位符的使用, 第三行代码说明了eval可以返回javascript虚拟机中持有的值, 第四行代码说明了一个值可以在C#, Javascript间反复传递。
-
null与undefined
Console.WriteLine(js.eval("null") == null); js.eval("print(<0> === null)", null); Console.WriteLine(js.eval("undefined") == DBNull.Value); js.eval("print(<0> === undefined)", DBNull.Value);
输出:
True
true
True
true
这就证明了javascript的null与C#的null完全等价,javascript的undefined与Js.undefined完全等价。
-
Unicode
js.eval("var 好好学习=<0>", "天天向上"); js.eval("print(好好学习)"); Console.WriteLine(js.eval("好好学习"));
输出:
天天向上
天天向上
这里可以看出,Unicode可以被正确支持。
-
执行代码片段返回JsObject
JsObject obj = (JsObject)js.eval("var t = { a: 'A', b :'B' }; t "); foreach (DictionaryEntry e in obj) Console.WriteLine(e.Key + ":" + e.Value);
输出:
a:A
b:B
继续执行:
obj.Remove("a"); obj.Add("c", "C"); js.eval("print (t.a)"); js.eval("print (t.c)");
输出:
undefined
C
这里可以看见,C#中的修改影响到了javascript对象的属性
继续执行:
js.eval("t.d = 100"); foreach (DictionaryEntry e in obj) Console.WriteLine(e.Key + ":" + e.Value);
输出:
b:B
c:C
d:100
这里可以看出,javascript内部修改对象属性,也反映到C#一边。
-
执行代码片断返回JsArray
JsArray a = (JsArray)js.eval("var o=[1,2]; o"); foreach (var v in a) Console.WriteLine(v);
输出:
1
2
继续执行:
a.Add(3); js.eval("print(o)");
输出:
1,2,3
这里可以看见,C#中的修改影响到了javascript数组的内容
继续执行:
js.eval("o.shift()"); foreach (var v in a) Console.WriteLine(v);
输出:
2
3
这里可以看出,javascript内部数组操作,也反映到C#一边。
继续执行:
Console.WriteLine(a.Count); a[5] = 100; js.eval("print(o)");
输出:
2
2,3,,,,100
这里可以看出,JsArray数组上的操作,符合javascript数组访问规范,不符合C#数组访问规范,不会抛出System.ArgumentOutOfRangeException异常。
继续执行:
a.Remove(DBNull.Value); js.eval("print(o)");
输出:
2,3,100
这里再次明确,C#的DBNull.Value等同于javascript的undefined,不存在的元素在javascript数组中表示为undefined。
继续执行:
JsObject o = a; o['c'] = 1000; Console.WriteLine(a.Count); Console.WriteLine(o.Count);
输出:
3
4
这里需要特别注意,对于javascript而言,Array也是Object,Array上也可以操作属性,通过JsObject或者JsArray两种方式引用同一对象,导致了不同行为。非特殊情况下,为了避免混乱,数组只用JsArray操作更容易理解。
-
执行代码片断返回JsFunction
JsFunction f = (JsFunction)js.eval("var func = function(a,b){ return a + b;}; func"); Console.WriteLine(f(10, 20));
输出:
30
这里可以看出,返回一个javascript函数在C#中使用非常容易。
-
Javascript对象操作
JsObject obj1 = (JsObject)js.eval("function CLS(n){this.value = n; this.dump = function(){print(this.value);}}; new CLS(200)"); ((JsFunction)obj1["dump"])();
输出:
200
这里可以看出,javascript代码用函数CLS创建了一个对象, 交由obj1持有, obj1上查询到函数属性dump进行调用, 输出创建出的对象value值200. C#的语法无法写成obj1.dump, 只好用索引化方式访问。
继续执行:
JsFunction cls = (JsFunction)js.eval("CLS"); JsObject obj2 = JsObject.create(cls, 400); ((JsFunction)obj2["dump"])();
输出:
400
第一行返回了前面创建的函数CLS, 第二行使用JsObject上的静态方法create创建对象obj2, 比较之前的代码, 可见JsObject.create等效于javascript代码中的new.
-
-