Values
editRead a value
A JsValue holds any JavaScript value. Its Kind tells which one, and the As* methods read it:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
static string Describe(JsValue value) => value.Kind switch
{
JsValueKind.Undefined => "undefined",
JsValueKind.Null => "null",
JsValueKind.Boolean => value.AsBoolean() ? "true" : "false",
JsValueKind.Number => value.AsNumber().ToString(CultureInfo.InvariantCulture),
JsValueKind.String => '"' + value.AsString() + '"',
JsValueKind.BigInt => value.AsBigInt().ToString(CultureInfo.InvariantCulture) + "n",
JsValueKind.Symbol => value.AsSymbol().ToString(),
JsValueKind.Object when value.IsCallable => "function",
JsValueKind.Object => "object",
_ => throw new UnreachableException(),
};
var values = (JsArray)realm.Execute("[42, 'text', true, null, undefined, 2n ** 70n, Symbol('id'), {}, Math.max]").AsObject();
using (engine.Enter())
{
var descriptions = new List<string>();
for (var i = 0u; i < values.Length; i++)
{
descriptions.Add(Describe(values.GetProperty(i.ToString(CultureInfo.InvariantCulture))));
}
Assert.Equal(["42", "\"text\"", "true", "null", "undefined", "1180591620717411303424n", "Symbol(id)", "object", "function"], descriptions);
}| Kind | Test | Read with |
|---|---|---|
| Undefined | IsUndefined | |
| Null | IsNull | |
| Boolean | IsBoolean | AsBoolean() |
| Number | IsNumber | AsNumber(): a double |
| String | IsString | AsString() |
| Symbol | IsSymbol | AsSymbol(): a JsSymbol |
| BigInt | IsBigInt | AsBigInt(): a BigInteger |
| Object | IsObject | AsObject(), or TryGetObject(), which returns null for the other kinds |
- IsNullOrUndefined tests both values that mean "no value", and IsCallable tests whether the value is a function.
- The
As*methods do not convert: they throw anInvalidOperationExceptionfor a value of another kind, for instance AsNumber on the string"5". TryGetNumber, TryGetString and TryGetBoolean return false instead. To accept any value, convert it as shown below. default(JsValue)isundefined.- ToString() of a JsValue is a description for debugging: it never runs code of the script.
Convert a value
To convert a value of any kind with the rules of JavaScript, as Number(x), String(x) and Boolean(x) do, use the conversions of the engine:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
// AsNumber, AsString... read a value of their kind: check the kind first
var length = realm.Execute("[1, 2, 3].length");
Assert.True(length.IsNumber);
Assert.Equal(3, length.AsNumber());
// The conversions of JavaScript accept any value, and may run code of the script (valueOf, toString)
var price = realm.Execute("({ amount: 5, valueOf() { return this.amount * 2; } })");
Assert.Equal(10, engine.ToNumber(price));
Assert.Equal("[object Object]", engine.ToString(price));
Assert.True(JsEngine.ToBoolean(realm.Execute("'0'")));
Assert.Equal(12, engine.ToNumber(realm.Execute("'12'")));engine.ToNumber, ToString, ToBigInt and ToPropertyKey can call thevalueOf,toStringorSymbol.toPrimitivemethods of an object, so they can run code of the script and throw a JsException. ToString throws aTypeErrorfor a symbol.engine.ToObjectwraps a primitive value in its object (new Number(1)...) and throws aTypeErrorforundefinedandnull.- JsEngine.ToBoolean never runs code.
- JsEngine.SameValue compares two values as
Object.isdoes. JsValue equality (==, Equals) is the same comparison. engine.IsArraytells whether a value is an array, asArray.isArraydoes.
Create a value
The numbers (int and double), the strings, the booleans, the objects and the symbols convert implicitly to JsValue. The other values have their own members:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
using (engine.Enter())
{
// The numbers, the strings and the booleans convert implicitly to JsValue
realm.GlobalObject.SetProperty("count", 3);
realm.GlobalObject.SetProperty("ratio", 0.5);
realm.GlobalObject.SetProperty("title", "Report");
realm.GlobalObject.SetProperty("enabled", true);
realm.GlobalObject.SetProperty("nothing", JsValue.Null);
realm.GlobalObject.SetProperty("big", JsValue.FromBigInt(BigInteger.Pow(2, 70)));
}
Assert.Equal("number,number,string,boolean,object,bigint", realm.Execute("[count, ratio, title, enabled, nothing, big].map(x => typeof x).join()").AsString());- JsValue.Undefined, JsValue.Null, JsValue.True and JsValue.False are the constants.
- JsValue.FromBigInt creates a BigInt; a
longconverts to a number, not to a BigInt. - A null JsObject converts to
null. - The well-known symbols are static properties of JsSymbol: JsSymbol.Iterator, JsSymbol.ToStringTag...
For objects and arrays, see Objects and arrays.
Symbols
A JsSymbol is a property key that cannot collide with a string. JsSymbol.Create creates a new symbol, as Symbol(description) does, and engine.GetRegisteredSymbol returns the symbol of Symbol.for(key):
using var engine = new JsEngine();
var realm = engine.CreateRealm();
// A new symbol is a unique property key
var secret = JsSymbol.Create("secret");
using (engine.Enter())
{
realm.GlobalObject.SetProperty(secret, 42);
realm.GlobalObject.SetProperty("secretKey", secret);
}
Assert.Equal(42, realm.Execute("globalThis[secretKey]").AsNumber());
Assert.Equal("undefined", realm.Execute("typeof globalThis[Symbol('secret')]").AsString());
// The symbols of the registry are the ones of Symbol.for, in all the realms of the engine
var shared = engine.GetRegisteredSymbol("app.id");
Assert.True(shared.IsRegistered);
Assert.Same(shared, realm.Execute("Symbol.for('app.id')").AsSymbol());
Assert.Same(shared, engine.CreateRealm().Execute("Symbol.for('app.id')").AsSymbol());- A symbol converts implicitly to a JsPropertyKey and to a JsValue.
- Two symbols are the same only when they are the same instance, even with the same description.
- The registry belongs to the engine: all its realms share it, and another engine returns another symbol for the same key. IsRegistered tells whether a symbol comes from a registry; its Description is then its key.
- The static properties of JsSymbol are the well-known symbols (Iterator, ToPrimitive, Species...), shared by all the engines.
- Unlike the objects, a symbol can be used by several engines.
Exchange dates
A JavaScript Date is a number of milliseconds since 1970-01-01T00:00:00Z. Create one with realm.CreateDate, and read one with TryGetDateValue:
using var engine = new JsEngine();
var realm = engine.CreateRealm();
using (engine.Enter())
{
// A Date is a number of milliseconds since 1970-01-01T00:00:00Z
var date = realm.CreateDate(new DateTimeOffset(2026, 10, 29, 12, 0, 0, TimeSpan.Zero).ToUnixTimeMilliseconds());
realm.GlobalObject.SetProperty("date", date);
}
Assert.Equal("2026-10-29T12:00:00.000Z", realm.Execute("date.toISOString()").AsString());
var returned = realm.Execute("new Date(Date.UTC(2000, 0, 1))").AsObject();
Assert.True(returned.TryGetDateValue(out var timeValue));
Assert.Equal(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero), DateTimeOffset.FromUnixTimeMilliseconds((long)timeValue));An invalid date has the time value NaN: check it before you convert it to a DateTimeOffset. For time zones and Temporal, see Intl, dates and time zones.