Objects are a kind of optimized hashtables. All fields names are hashed into an integer value that is used as the key in a lookup table. Insertion of a new field is O(n)
, access to a field is O(log n)
. If you're generating from a statically typed language, you might prefer arrays for storing fields, since they provide O(1)
access.
To create an object, you can use the builtin $new
, that can either return a copy of an object or a new object :
o = $new(null); // new empty object
o2 = $new(o); // makes a copy of o
o2 = $new(33); // if parameter is not an object, throw an exception
You can set fields of an object using dot access or use the builtin $objset
:
o.field = value;
$objset(o,$hash("field"),value);
You can access object fields for reading using either dot access or builtin $objget
:
o.field; // returns "field" value of object o
$objget(o,$hash("field")); // returns "field" value of object o
Please note that the second parameter of $objset
and $objget
is hashed at runtime, so it's a bit less efficient of an operation than dot access, but enables introspection.
If a field is not defined when accessed for reading, the null
value is returned. If a field doesn't exists when a field is accessed for writing, the field is added.
To check for the existence of a field, you can use the $objfield
builtin that checks if an object o has a given field, even if that field is set to the null
value :
$objfield(o,$hash("field")); // true if o have "field"
$objfield(null,33); // false
You can remove an object field with the $objremove
builtin :
$objremove(o,$hash("field")); // remove "field" from o
One other way to declare objects is to use the following notation, which is more efficient when you want initialize several fields at the same time :
var o = {
x => 0,
y => -1,
msg => "hello"
}
When a function is called using the dot access or the builtin $objcall
, the function can access a special variable called this
, which is the "object context" (the object with which the function was called) :
o = $new(null);
o.x = 1;
o.add = function(y) { return this.x + y; }
$print(o.add(2)); // prints 3
$print( $objcall(o,$hash("add"),$array(2)) ); // prints 3
The context is set when an object function is called and can be accessed from any sub-function :
foo = function() {
$print(this.x);
}
o = $new(null);
o.x = 3;
o.bar = function() { foo(); };
o.bar(); // prints 3
You can modify the value of this
at runtime by simply assigning it to another value; it can be set to any value, not just objects. When returning from an object call, the context is restored, so any modifications are lost :
this = 1;
o.foo = function() {
// here , we have this = o;
this = 2; // modify
};
o.foo();
$print(this); // 1
As explained before, fields names are first hashed into integer values for faster access. In order to avoid collisions in the hashing functions, they are then stored in a global table then checked that hash(x) = hash(y) implies x = y. The other utilities of this library are to be able to reverse the hashing function at runtime. For example, this can be useful to print the field names of an object for debugging purposes.
You can use the following builtins : $hash
returns the integer hashing value of a string, or raise an exception in the case of a collision. $field
transforms an integer into a previously hashed string or returns null
.
The builtin $objfields
returns an array containing all fields identifiers for the given object :
var a = $objfields(o);
var i = 0;
while( i < $asize(a) ) {
var fname = $field(a[i]);
var fval = $objget(o,a[i]);
$print( fname + " = " + fval + "\n" );
i = i + 1;
}
Several operators can be overloaded so when they're applied to objects, they are actually calling methods. Here's a list of overloadable operators and corresponding methods names :
%%__string%%
method on the object with no arguments. A string should be returned.%%__compare%%
method is called on the first object with the second object as a parameter.a + b
, if a
is an object, a.%%__add%%(b)
is called, otherwise if b
is an object, b.%%__radd%%(a)
is called.%%__sub%%
and %%__rsub%%
.%%__mult%%
and %%__rmult%%
.%%__div%%
and %%__rdiv%%
.%%__mod%%
and %%__rmod%%
.a[i]
actually calls a.%%__get%%(i)
.a[i] = v
actually calls a.%%__set%%(i,v)
.If the overloaded field is not defined when an operation occurs, an exception is raised.
Each object can have a prototype which is also an object. When a field is accessed for reading and is not found in an object, it's searched recursively in its prototype.
Prototypes can be accessed using $objgetproto
and $objsetproto
:
var proto = $new(null);
proto.foo = function() { $print(this.msg) }
var o = $new(null);
o.msg = "hello";
$objsetproto(o,proto);
o.foo(); // print "hello"
$objsetproto(o,null); // remove proto
o.foo(); // exception