Nombas > SE:ISDK DevSpace > Tips > Creating Dynamic Objects

 

Creating Dynamic Objects

Contents

Dynamic Objects
Writing Dynamic Functions
Available Dynamic Properties
Building Your Own Classes
Prototype Vs _Prototype, Or What's The Deal With This?
Now Back To The Program
Let's Be Courteous, Ok?
Growth, Expansion, World Domination... Muhahahaha
Starting From Scratch
Your Grandfather's Pretty Stingy, Or Why Dynamic Properties Don't Inherit
Leave Me Alone, You Bother Me, Kid

DYNAMIC OBJECTS

ScriptEase allows the Javascript programmer to change how an object's properties are accessed. Let's say you have an object 'x' which you created using this line of code:

var x = new Object();

Normally, whenever any property of 'x' is accessed or written to, the interpreter runs its internal code which looks up the value of the given property or changes it. For example, if you use:

var y = x.foo;

the internal code checks to see if 'x' has a property called 'foo' and if so, assigns it to 'y', otherwise it assigns the undefined value to 'y'.
 

That is the usual behavior that everyone expects. However, suppose instead you want to create a special object that doesn't do these normal things. Instead, lets say that you want to create a new object 'x' that behaves differently.
'x' will have only the property 'color', and if you assign to it, you want the color displayed by your monitor to change. If you read from the property 'color', you want it to check what the current color is and return it. In this way, you can write programs that fight with each other changing the color of your screen (what fun!). This capability is what dynamic objects give you.

CREATING A DYNAMIC OBJECT

What makes a dynamic object special? Good question. A dynamic object is just like any other object, except that it has some special properties which tell the ScriptEase processor how to perform the tasks you want. The simplest
dynamic property is '_get'. When any member of an object (except the dynamic properties) is read from, if the object has a '_get' property, it is used to get the value of the property. Here is a short example:

function my_get(property)
{
  Clib.printf("Someone tried to read property '%s'\n",property);
  return 10;
}
 

var x = new Object();
x._get = my_get;
 

That's it, now 'x' is a dynamic object. Whenever anyone tries to read a property of 'x', your function will be called. So, if someone wrote 'a = x.foo', they are trying to access the property 'foo' of 'x', and your function is called. The parameter 'property' is a string with the text of the property being accessed. The 'this' value is set the object being manipulated. So in this example, 'property' would have the value "foo" and 'this' would be the same as 'x'. This example '_get' property prints the name of the property being accessed and then returns 10. So for this object, all properties have the value of 10 and print a short message when they are accessed.

WRITING DYNAMIC FUNCTIONS

A couple of notes are in order. First, you may use the ScriptEase toolkit API to perform the same tasks. jseCreateWrapperFunction() and jseMemberWrapperFunction() will be useful in getting the function you want to be called assigned to the member. Second, both API and script code performs identically; whatever value you return from your function is the value that the member has THIS TIME. If someone accesses the member again, and you return a different value, that is OK! Remember that in ECMAScript, all functions return a value. If you don't explicitly return one using the return statement (or the equivelent API call), the undefined value is returned.

I mentioned before that '_get' is not used to look up dynamic properties. That means that when the interpretter is looking to see if your object has a '_get' property, it doesn't try to use your '_get' to do it (which wouldn't make much sense.) It doesn't use '_get' for any other dynamic property either.

Finally, what if you want to access the real members in your object inside your '_get' function? You might think that doing so would cause your '_get' to be called again, and so on, leading to a big mess. Fortunately that is not the case. Inside any dynamic function, that particular dynamic function is turned off. So inside '_get', if you access a member of your object, it does the 'normal' thing. The standard way to read a property inside your object is to use 'this[property]'. If you want to write what I've referred to as the 'normal' thing in ScriptEase, you might do this:
 

function normal_get(property)
{
   return this[property];
}

var x = new Object();
x._get = normal_get;
 

This dynamic '_get' property just returns that value of the property exactly like what normally happens (except that it takes a lot longer).

Please note that only the dynamic property being called is shut off. So if in your '_get' property you assign a value to a property, your dynamic '_put' function will be called if it exists.

AVAILABLE DYNAMIC PROPERTIES

What object behaviors can you change? Glad you asked. What follows is a description of each dynamic property, including the property name you must assign it to, what parameters it receives, and what the heck it is supposed to do. For all dynamic properties, 'this' is set to the object being manipulated. Finally, the dynamic property replaces the existing behavior; it is not in addition to it.
 

SYNTAX

function ._get(property)

DESCRIPTION

The '_get' dynamic property is called whenever any property of the object is read from. It is passed the property name being accessed as a parameter. Whatever value is returned is taken to be the properties' value THIS TIME. If you don't explicitly return a value, the undefined value is returned.

 

SYNTAX

function ._put(property,value)

DESCRIPTION

The '_put' dynamic property is called whenever any property is assigned a new value. The first parameter is the property name being assigned and the second parameter is the actual value being assigned. The value could be anything, from a number to a string to an object. No return value is expected and is ignored if you provide one. The default behavior is to just set the value if the property in question is not read-only. If it is read-only, nothing is done, but this is not considered an error.

 

SYNTAX

function ._canPut(property)

DESCRIPTION

Before any value is put into a property, if you have a '_canPut' dynamic property, it is called. If you return False, the attempt to put the value is ignored (but it is not considered to be an error.) If True is returned, the put process continues as normal. You can use this dynamic property as a security guard to not allow certain properties to be changed, or to put up a dialog box asking for a password for examples. The default behavior for this is to always return True. If you return no value, True is assumed.

 

SYNTAX

function ._hasProperty(property)

DESCRIPTION

When Javascript it trying to resolve a variable name in the script, it calls the 'hasProperty' property of each object in the scope chain. If the object has the property in question, True is returned. The object's prototype is also searched. If you return no value, False is assumed. 

NOTE: It is unlikely your object will ever end up in the scope chain, so this property will probably never be used.

 

SYNTAX

function ._delete(property)

DESCRIPTION

The '_delete' dynamic property is used whenever the delete operator is used to delete an object's property.  The default behavior is to remove the specified property if it exists and is not marked as jseDontDelete. 

When an object itself is deleted (such as going out of scope), the object's members are NOT deleted individually.  However, the 'delete' property will be called with "_delete" as the property name. This is a special courtesy to allow object writers to destroy objects and can be considered a destructor. However, if an object is part of a cyclic loop there is no guarantee that the destructor will be called. Currently, it is called in most cases, but that may change in a later version. For example: 
 

var a.x = a; 
a._delete = my_delete_function; 
 

In this case, 'a' may never have its destructor called. You have been warned. 
 

A note to toolkit users: jseDontDelete is checked only by the Javascript language delete operator, not by the jseDeleteMember() API call. 

 

SYNTAX

function ._defaultValue([hint])

DESCRIPTION

When an object is converted to a primitive type, this routine is called to get its value. A single parameter is optionally passed to it, called 'hint'. 'hint' is either a string or a number. Its value is unimportant, only its type is; the 'hint's type is the same as what we would like the return from '_defaultValue' to be. For example, if the hint is a string, we would like a string. You may check the type of the 'hint' by using 'typeof hint'. 

The default behavior is to check the object for either a 'toString()' method or a 'valueOf()' method, and call those if found. If the hint is a string, 'toString()' is looked for first, else 'valueOf()' is looked for first. 

These two functions are not internal functions, so '_defaultValue' searches the prototype for them. The easiest way to implement this functionality is to leave '_defaultValue' alone and simply provide these two functions in your prototype.

 

SYNTAX

function ._construct(...)

DESCRIPTION

When an object is used as a constructor, it can only do so if it has the '_construct' property. By default, the '_construct' property is the same as the '_call' property, but it can be changed. Whatever parameters were part of the 'new' call are passed along.

 
'this' is setup already as a blank slate object with the prototype already copied in. You can just fill it in. If you return no value or don't return an object, the filled-in 'this' object becomes the result of the 'new' call. Alternately, you can return an object which becomes the result. 

 

SYNTAX

function ._call(...) 

DESCRIPTION

Whenever a function is called, the function's '_call' property is used to determine what routine to call. You may set up any object's '_call' dynamic property to point to a function to be called. Whatever parameters were passed to the call are received. Whatever value is returned from the call is the result of the call. 

 

BUILDING YOUR OWN CLASSES
 
Using the capabilities of ScriptEase, it is easy to build your own classes. You can build a class from scratch by defining a new variable and filling in all of the fields as described below. Alternately, you can start building by declaring a function with the name of your class. That function will automatically become the constructor for your new class.

The most basic operation that your new class will perform is to construct new objects of your class. Let us start with a minimal beginning:
 

   function MyClass()
 {
 }

 MyClass.prototype.foo = 10;
 

Now, we can construct an object of MyClass by using this code:
 

   var myobj = new MyClass();
 

What actually happens in this call? First a new object is created which will be the object that is generated by the new operator. Next, its internal prototype property (_prototype) is set to point to the same object as 'MyClass.prototype'. Finally, MyClass() is called with this newly constructed object as the 'this' variable. MyClass() could do any initialization of this object before it returns.
 
PROTOTYPE VS _PROTOTYPE, or WHAT'S THE DEAL WITH THIS?

The difference between 'prototype' and '_prototype' can be confusing. After all, they look similar. Let's start with '_prototype.' EVERY object has an '_prototype' property. Whenever a member of that object is read from, if the object doesn't seem to contain that property, the '_prototype' object is searched to see if it contains it, and if it does, we use that one. This allows a form of inheritance. If that object doesn't contain it, its '_prototype' is searched, and so on until a '_prototype' that is null is encountered, which signals the end of the chain.

So what the heck is 'prototype' without the underscore? Think of  'prototype' as a construction area. You use it to set up the '_prototype' of any object derived from it using the new operator. Whenever any object is created using new, its '_prototype' is set to point back to the 'prototype' of the parent. A bit confusing, huh? Its best to forget about '_prototype' and just remember that you set up the 'prototype' of the new class you are building and when you are done, objects created from that class will inherit that prototype. 

NOW BACK TO THE PROGRAM
 
So the example we gave above constructs a new object, but the constructor doesn't do anything with it. The only difference between this object and any old generic object so far is that its '_prototype' points to an object with one member: foo. That means that this object will act just like any old other object, except if someone tries to read its member 'foo', it will find that it is already there, inherited through the prototype. In fact, all members of this class will have that same member foo. It would be a good idea to grab a book on Javascript to see how prototypes work and some good uses you can put them too. The most common use it to stick in functions which you'd like all members of the object to have.

LET'S BE COURTEOUS, OK?

All of the native Javascript objects have a _class property. That is just a text string that holds the name of the class.
It isn't used for anything by the language, but it is a courtesy to programmers who might want to use your class; if something isn't happening the way they expect, they can easily print out the class string to find out what is going wrong (huh, I expected class "Foo" but it is "String" instead - oh, I see, I used the wrong variable... oops.) You can do this by simply assigning an appropriate text string in the prototype like this:
 

   MyClass.prototype._class = "MyClass";

Although class looks like a dynamic property because of the '_', it is never used by the Javascript interpretter. This allows you to treat it just likely a regular class member and inherit it through the prototype since you will be the only one ever accessing it. If you want, you can set the _class on each individual object in the constructor instead.

 
GROWTH, EXPANSION, WORLD DOMINATION... MUHAHAHAHA

Let's expand our class to have a function like we talked about above. Also, we'll make sure it is only called on objects of our class.
 

   function foo()
 {
    Clib.assert( this._class=="MyClass" );
    Clib.printf("Hi there!\n");
 }
 MyClass.prototype.hello = foo;
 

There, add that to the previous code, and now new class objects have a function that can be called! You'd do it just like this:
 

 myobj.hello();

 
STARTING FROM SCRATCH

You may have noticed that you can still call MyClass() just like any other function, only in this case the 'this' property is set to the global object. That's because generic functions have both a '_call' and '_construct' method. In each case, both are just set up to call the function. BUT, you can redirect either, so that a different function will be invoced for a call than will be for a constructor. The easiest way is to start with a blank object and explicitly assign these properties.
 

   function MyClassConstructor()
  {
  }

  var MyClass;
  MyClass._construct = MyClassConstructor;
 

There, now your class exists, and new objects can be constructed from it. However, if anyone tries to call it, an error will be generated. You are safe to play around with the 'this' if you want because you know it will always be a newly constructed object ready for you to extend.

It is possible to completely ignore the 'this' object in a constructor and build your own up from scratch! To do this, simple build an object in any way you like and return it (using the return statement.) It will be the result of the constructor and the original object will be thrown away.
 
YOUR GRANDFATHER'S PRETTY STINGEE, or WHY DYNAMIC PROPERTIES DON'T INHERIT

You want your class objects to have a dynamic property, perhaps a '_get'. Ahah, you say! I'll stick it in the prototype and all my objects will inherit it. Unfortunately, you try it and it doesn't work. There are a number of reasons for this. First, it makes it hard to track bugs, since a user may have an object that doesn't seem to have a dynamic property, but acts weird nonetheless. Second, the dynamic properties are just the regular Javascript internal properties given accessable names. Although it might be reasonable to search for them in the prototype, it is just as reasonable to treat them as internal to the particular object. Third, performance would be negatively impacted by having to search prototype chains for these properties. Thus, the current decision is that they DON'T inherit.

So, how do you get around this? Simple, inside your constructor you can do any initializations on the object before you finish. So you might write your constructor like this:
 

   function MyClassConstructor()
  {
     this._get = MyDynamicGetFunction;
  }
 
LEAVE ME ALONE, YOU BOTHER ME, KID.

Well, that's it, all the information you need to start writing your own classes, perhaps that even use dynamic properties.

To help you, Nombas has written a number of sample classes in both Javascript and using the C++ ScriptEase ISDK toolkit API. These dynamic-object samples ship with the ISDK as files ISDK\SAMPLES\OBJSAMPS\OBJECT?.C.

For ScriptEase:Desktop you can find JavaScript source versions of these samples as OBJECT?.JSE. Take a look at them to see how you might go about writing your own.

Home | Scripting | Products | Purchase | Download | Support | Company

Copyright ©2001, Nombas, Inc. All Rights Reserved.
Questions? Visit
http://support.nombas.com/