Functions
editExpose a .NET function
realm.CreateFunction creates a JavaScript function implemented by a JsNativeFunction delegate. Store it in a property, such as a property of the global object, to make it visible to the scripts:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
using (engine.Enter())
{
// The name and the length are the name and length properties of the function
realm.GlobalObject.SetProperty("formatPrice", realm.CreateFunction("formatPrice", 2, (thisValue, arguments) =>
{
// The arguments are the ones of the call: a script can pass fewer than the length of the function
if (arguments.Length < 1 || !arguments[0].IsNumber)
{
throw realm.CreateException(JsErrorType.TypeError, "The amount must be a number");
}
var currency = arguments.Length > 1 ? engine.ToString(arguments[1]) : "EUR";
return arguments[0].AsNumber().ToString("F2", CultureInfo.InvariantCulture) + " " + currency;
}));
}
Assert.Equal("12.50 EUR", realm.Execute("formatPrice(12.5)").AsString());
Assert.Equal("3.00 USD", realm.Execute("formatPrice(3, 'USD')").AsString());
Assert.Equal("formatPrice,2", realm.Execute("[formatPrice.name, formatPrice.length].join()").AsString());
Assert.Equal("TypeError: The amount must be a number", realm.Execute("try { formatPrice('x'); } catch (e) { `${e}` }").AsString());- The delegate receives the
thisvalue of the call and the arguments, and returns the result. Return JsValue.Undefined for a function that returns nothing. argumentsholds the arguments that the script passed: there can be fewer or more than the length of the function. Checkarguments.Lengthbefore you read an argument.- Check the kind of an argument before you read it with AsNumber, AsString..., or convert it with
engine.ToNumber,engine.ToString... as the built-ins do. See Values. - Throw a JavaScript error with
realm.CreateExceptionfor an invalid argument. See Handling errors. - The function runs inside the scope of the engine, in its realm: it can read and write properties and create objects directly.
realm.CreateFunction also accepts a typed Func<...> or Action<...>, whose arguments and result are converted for you. See Typed functions.
A function created by CreateFunction cannot be called with new. To create a constructor, see Exposing .NET objects.
Call a JavaScript function from .NET
engine.Call calls a function with a this value and arguments; engine.Construct calls a constructor as new does:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
realm.Execute("""
function applyDiscount(order, percent) {
return { ...order, total: order.total * (1 - percent / 100) };
}
""");
using (engine.Enter())
{
var order = realm.CreateObject();
order.SetProperty("id", "A-12");
order.SetProperty("total", 80);
// Call(function, this, arguments...)
var applyDiscount = realm.GlobalObject.GetProperty("applyDiscount");
var discounted = engine.Call(applyDiscount, JsValue.Undefined, order, 25).AsObject();
Assert.Equal("A-12", discounted.GetProperty("id").AsString());
Assert.Equal(60, discounted.GetProperty("total").AsNumber());
// Construct(constructor, arguments): new Map([["a", 1]])
var map = engine.Construct(realm.GlobalObject.GetProperty("Map"), [realm.CreateArray([realm.CreateArray(["a", 1])])]).AsObject();
Assert.Equal(1, map.GetProperty("size").AsNumber());
}- Pass JsValue.Undefined as
thisfor a plain function call, or the object for a method call (engine.Call(obj.GetProperty("method"), obj, ...)). - Call throws a JsException with a
TypeErrorwhen the value is not a function, and the exceptions that the function throws. Test IsCallable first when the value comes from a script. - A Call or a Construct made while no script runs is an entry into JavaScript: the limits of the engine apply to it, as to a script. See Limits and sandboxing.
- Construct accepts a
newTarget, asReflect.constructdoes.
Functions and realms
A function created by realm.CreateFunction runs in its realm, even when a script of another realm calls it: the objects that it creates belong to its realm. Pass runInCallerRealm: true to run it in the realm of its caller instead, for a function shared by several realms.