.NET 全面拥抱 Javascript,Jint 火了

家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!

1.什么是 Jint

Jint 是 .NET 的 Javascript 解释器,可以在任何现代 .NET 平台上运行,支持 .NET Standard 2.0 和 .NET 4.6.2 目标(及更高版本)。在性能方面,Jint 也是非常优秀:

  • 由于 Jint 既不生成任何 .NET 字节码也不使用 DLR,因此可以非常快地运行相对较小的脚本
  • 如果重复运行相同的脚本,则应该缓存 Esprima 生成的脚本或模块实例并将其提供给 Jint 而不是内容字符串
  • 更适合在严格模式下运行引擎,可以提高性能

Jint 可以适应以下典型场景:

  • 在安全的沙盒环境中在 .NET 应用程序内运行 JavaScript
  • 将本机 .NET 对象和函数公开给 JavaScript 代码,以 JSON 形式获取数据库查询结果、调用 .NET 方法等
  • 支持 .NET 应用程序中的脚本编写,允许用户使用 JavaScript 自定义应用程序(如 Unity 游戏)

Jint 的一些用户包括 RavenDB、EventStore、OrchardCore、ELSA Workflows、docfx、JavaScript Engine Switcher 等等。

目前 Jint 在 Github 通过 BSD-2-Clause 协议开源,有超过 3.7k 的 star,1k 的 fork、1.7k 的项目依赖量,是一个值得关注的开源项目。

2.如何使用 Jint

基础示例

下面示例定义了一个名为 log 的新值,该值指向 Console.WriteLine,然后运行一个调用 log('Hello World!') 的脚本。

var engine = new Engine()
    .SetValue("log", new Action<object>(Console.WriteLine));

engine.Execute(@"
    function hello() {
        log('Hello World');
    };

    hello();
");

变量 x 设置为 3,并且 x * x 在 JavaScript 中计算。结果直接返回到 .NET,在本例中为双精度值 9。

var square = new Engine()
    .SetValue("x", 3) // define a new variable
    .Evaluate("x * x") // evaluate a statement
    .ToObject(); // converts the value to .NET

还可以直接传递 POCO 或匿名对象并从 JavaScript 使用。例如,在此示例中,一个新的 Person 实例是通过 JavaScript 操作的。

var p = new Person {
    Name = "Mickey Mouse"
};

var engine = new Engine()
    .SetValue("p", p)
    .Execute("p.Name ='Minnie'");

Assert.AreEqual("Minnie", p.Name);

可以调用 JavaScript 函数引用:

var add = new Engine()
    .Execute("function add(a, b) { return a + b; }")
    .GetValue("add");

add.Invoke(1, 2); // -> 3

或者通过函数名称调用:

var engine = new Engine()
   .Execute("function add(a, b) { return a + b; }");

engine.Invoke("add", 1, 2); // -> 3

访问 .NET 程序集和类

开发者可以通过如下配置引擎实例来允许引擎访问任何 .NET 类:

var engine = new Engine(cfg => cfg.AllowClr());

然后就可以将系统命名空间作为全局值进行访问。以下是它在命令行实用程序上下文中的使用方式:

jint> var file = new System.IO.StreamWriter('log.txt');
jint> file.WriteLine('Hello World !');
jint> file.Dispose();

国际化

如果不想使用计算机的默认值,则可以在使用区域设置 JavaScript 方法时强制引擎应使用的时区或文化,下面示例强制将时区设置为太平洋标准时间。

var PST = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
var engine = new Engine(cfg => cfg.LocalTimeZone(PST));

engine.Execute("new Date().toString()"); // Wed Dec 31 1969 16:00:00 GMT-08:00

下面示例使用法语作为默认区域性:

var FR = CultureInfo.GetCultureInfo("fr-FR");
var engine = new Engine(cfg => cfg.Culture(FR));

engine.Execute("new Number(1.23).toString()"); // 1.23
engine.Execute("new Number(1.23).toLocaleString()"); // 1,23

使用模块

可以使用模块从多个脚本文件导入和导出变量:

var engine = new Engine(options =>
{
    options.EnableModules(@"C:\Scripts");
})

var ns = engine.Modules.Import("./my-module.js");

var value = ns.Get("value").AsString();

默认情况下,模块解析算法将仅限于 EnableModules 中指定的基本路径,并且没有包支持。但是,开发者可以通过两种方式提供自己的包。 使用 JavaScript 源代码定义模块:

engine.Modules.Add("user", "export const name ='John';");

var ns = engine.Modules.Import("user");

var name = ns.Get("name").AsString();

或者使用模块构建器定义模块,其允许从 .NET 导出 CLR 类和值:

// Create the module 'lib' with the class MyClass and the variable version
engine.Modules.Add("lib", builder => builder
    .ExportType()
    .ExportValue("version", 15)
);

// Create a user-defined module and do something with 'lib'
engine.Modules.Add("custom", @"
    import {MyClass, version} from 'lib';
    const x = new MyClass();
    export const result as x.doSomething();
");

// Import the user-defined module; this will execute the import chain
var ns = engine.Modules.Import("custom");

// The result contains "live" bindings to the module
var id = ns.Get("result").AsInteger();

3.Jint 的安全性

以下功能为开发者提供了一个安全的沙盒环境来运行用户脚本:

  • 定义内存限制,以防止分配耗尽内存。
  • 启用 / 禁用 BCL 的使用以防止脚本调用 .NET 代码。
  • 限制语句数量以防止无限循环。
  • 限制调用深度以防止深度递归调用。
  • 定义超时,以防止脚本花费太长时间才能完成。

4.本文总结

本文主要和大家介绍 Jint ,其是 .NET 的 Javascript 解释器,可以在任何现代 .NET 平台上运行,支持 .NET Standard 2.0 和 .NET 4.6.2 目标(及更高版本)。因为篇幅问题,关于 Jint 主题只是做了一个简短的介绍,但是文末的参考资料提供了大量优秀文档以供学习,如果有兴趣可以自行阅读。如果大家有什么疑问欢迎在评论区留言。

参考资料

https://github.com/sebastienros/jint

https://blog.devgenius.io/a-javascript-rules-engine-in-net-6-fb092cdc44c

https://github.com/topics/jint

原文链接:,转发请注明来源!