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.


上一页 下一页