.NET 全面擁抱 Javascript,Jint 火了

2024年2月6日 25点热度 0人点赞

傢好,很高興又見面了,我是"高級前端‬進階‬",由我帶著大傢一起關註前端前沿、深入前端底層技術,大傢一起進步,也歡迎大傢關註、點贊、收藏、轉發!

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<MyClass>()
    .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