Custom Objects and Classes for Scripting 2005-03-26 - By Bradley R. Gabe
Back
>which makes sense. But I was wondering about the instantiation of the >object itself. First of all, what are some of the best standards and >practices of creating objects in jscript? My understanding is that there >are a couple of ways to do it and I was wondering if anyone has any reason >why one way in particular is better for XSI.
From recent experience, I've learned that it is necessary to structure your object instatiation such that you can use the same call inside XSI, or ouside XSI in javascript land for netview development. For typical javascript object instatiation, after the class file is included into the current script, the following syntax is required:
//Instantiate an included custom object in a netview script var obj = new CustomObject();
In XSI, including a file into your current script is slow compared to just registering the command and calling it directly. Since calling commands is an XSi thing and not a jscript thing, jscript is not going to recognize your custom command with respect to the 'new' call. Your command has to return the new instance so you don't need the 'new' call:
//Instantiate a custom object from an XSI command var obj = CustomObject();
As an addendum to my blog entry, I need to talk about structuring the instantiate such that your custom objects can work inside XSI, or inside netview javascript in a seamless fashion. While I suspect there are more elegant ways to do it (perhaps going all Python would be one), I've found that I can overcome the difference between XSI and javascript object instantiation by splitting my constructor into 2 separate functions. This allows me to use the exact same call, whether I'm running from within an XSI command or from a javascript doing a netview.
The first function is a class template which only defines the properties and methods. The second function does the instantiation and construction and returns the new class instance.
From within XSI, I parse the command and tell it to run the second function as the command procedure. From within javascript, after the file is included, I can run the second function the same way I would within XSI.
The other thing I've discovered is that a separate constructor function makes it easier to overload your object input and decide whether or not to return an object if the input is bad. In the example below, I use the constructor function to test the input and set class properties based on input type, or log errors and return a null object if the input is invalid:
// CLASS TEMPLATE function Class_CustomObject(inputValue) { // Class Properties -- ---- ---- ---- ---- ------ this.type = 'CustomObject'; this.size = 0; this.Model = null; this.description = '';
// Class Methods -- ---- ---- ---- ---- -- this.SetSize = CustomObject_SetSize; this.GetModel = CustomObject_GetModel; this.LogError = CustomObject_LogError; }
// CLASS CONSTRUCTOR function CustomObject(inputValue) { // Get pointers to application for netview var xsi = new ActiveXObject('XSI.Application'); var app = xsi.Application;
// Instantiate a new object var obj = new CustomObject();
// Based on inputValue, return something -- ---- ---- ---- ---- ------
// If user provides no input, do not instantiate if(!inputValue) return(null);
// If user inputs a number, set the size property if(typeof(inputValue) == 'number'){ obj.size = inputValue; return(obj); }
// If user inputs an object and it is a #model, point the Model property to it else if(typeof(inputValue) == 'object' && inputValue.type == '#model){ obj.Model = inputValue; return(obj); }
// If we got this far, input is not a valid type app.LogMessage('CustomObject(Argument[0]) -- Invalid input', 2); return(false); }
>I intend to roll an object as a utility library of functions (as suggested >as one possible use in the blog entry). If I'm understanding the example, >I think thats what GUITools is doing as well. Am I to understand that the >object is instantiated every time the "GUITools" command is called? Or is >it cache'ing it somewhere in global memory the first time and just passing >the same instance on subsequent calls? Or is the instantiation so cheap, >it doesn't matter that you instantiate it in every function you use it >in? Is there a good place to toss an object where it will persist for the >remainder of the given XSI session? Or is that foolhearty?
I ran into some problems with this technique recently after stacking too many objects too deep. While it was never an issue in XSI 3.5 on linux, it seems that with the changes to command registration in XSI 4+ and on windows, one must be cautious about treating jscript too much like a real programming language.
An important aspect to know about jscript is that its object functionality is a bit of a hack. In C++, Java, and other compiled languages when you instantiate an object, you get a separate memory space and control over your construction and destruction. Inheritance and polymorphism is stable and reliable to many levels deep.
In jscript, the whole object functionality is a clever hack in the language, using arrays and string copies of your functions. Methods, for example, are simply pointers to your function stored as a string in an array. When you call the method, jscript runs an eval() on your function string, then runs the function. When you include an ancestor object into your current object, jscript loads in the file and does string concatenation at runtime.
What this means is that you should be really careful about nesting objects inside other objects too many levels deep. 2 or 3 levels is okay, but any more than that and you are risking a jscript meltdown at runtime. Test your stuff early and often, especially for performance in order to prevent unexpected problems. The moment you run into strange and unexplainable variable scope issues, you've gone too many levels deep and you'll have to explode some of your earliest objects.
Other scripting languages, such as Python and Php are built around this kind of object component construction, and don't suffer the same isses. However, I have heard some rumors about a compilable version of jscript, so perhaps the future for jscript and OOP may be better.
>thanks, >-brad >--- >Unsubscribe? Mail Majordomo@(protected) with the following text in body: >unsubscribe xsi
<html> <body> <blockquote type=cite class=cite cite="">which makes sense. But I was wondering about the instantiation of the object itself. First of all, what are some of the best standards and practices of creating objects in jscript? My understanding is that there are a couple of ways to do it and I was wondering if anyone has any reason why one way in particular is better for XSI.</blockquote><br> From recent experience, I've learned that it is necessary to structure your object instatiation such that you can use the same call inside XSI, or ouside XSI in javascript land for netview development. For typical javascript object instatiation, after the class file is included into the current script, the following syntax is required:<br><br> <font face="Courier New, Courier" color="#008000">//Instantiate an included custom object in a netview script<br> </font><font face="Courier New, Courier">var obj = new CustomObject();<br><br> </font>In XSI, including a file into your current script is slow compared to just registering the command and calling it directly. Since calling commands is an XSi thing and not a jscript thing, jscript is not going to recognize your custom command with respect to the 'new' call. Your command has to return the new instance so you don't need the 'new' call:<br><br> <font face="Courier New, Courier" color="#008000">//Instantiate a custom object from an XSI command<br> </font><font face="Courier New, Courier">var obj = CustomObject();<br><br> </font>As an addendum to my blog entry, I need to talk about structuring the instantiate such that your custom objects can work inside XSI, or inside netview javascript in a seamless fashion. While I suspect there are more elegant ways to do it (perhaps going all Python would be one), I've found that I can overcome the difference between XSI and javascript object instantiation by splitting my constructor into 2 separate functions. This allows me to use the exact same call, whether I'm running from within an XSI command or from a javascript doing a netview.<br><br> The first function is a class template which only defines the properties and methods.<br> The second function does the instantiation and construction and returns the new class instance.<br><br> From within XSI, I parse the command and tell it to run the second function as the command procedure.<br> From within javascript, after the file is included, I can run the second function the same way I would within XSI.<br><br> The other thing I've discovered is that a separate constructor function makes it easier to overload your object input and decide whether or not to return an object if the input is bad. In the example below, I use the constructor function to test the input and set class properties based on input type, or log errors and return a null object if the input is invalid:<br><br> <font face="Courier New, Courier" color="#008000">// CLASS TEMPLATE<br> </font><font face="Courier New, Courier">function Class_CustomObject(inputValue)<br> {<br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// Class Properties -- ---- ---- ---- ---- ------<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>this.type = 'CustomObject';<x-tab> </x-tab><br> <x-tab> </x-tab>this.size = 0;<br> <x-tab> </x-tab>this.Model = null;<br> <x-tab> </x-tab>this.description = '';<br><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// Class Methods -- ---- ---- ---- ---- --<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>this.SetSize = CustomObject_SetSize;<br> <x-tab> </x-tab>this.GetModel = CustomObject_GetModel;<br> <x-tab> </x-tab>this.LogError = CustomObject_LogError;<br> }<br><br> </font><font face="Courier New, Courier" color="#008000">// CLASS CONSTRUCTOR<br> </font><font face="Courier New, Courier">function CustomObject(inputValue)<br> {<br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// Get pointers to application for netview<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>var xsi = new ActiveXObject('XSI.Application');<br> <x-tab> </x-tab>var app = xsi.Application;<br><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// Instantiate a new object<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>var obj = new CustomObject();<br><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// Based on inputValue, return something -- ---- ---- ---- ---- ------<br><br> </font><font face="Courier New, Courier"><x-tab> </x-tab></font><font face="Courier New, Courier" color=" #008000">// If user provides no input, do not instantiate<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>if(!inputValue) return(null);<br><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// If user inputs a number, set the size property<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>if(typeof(inputValue) == 'number'){<br> <x-tab> </x-tab><x-tab> </x-tab>obj.size = inputValue;<br> <x-tab> </x-tab><x-tab> </x-tab>return(obj);<br> <x-tab> </x-tab>}<br><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// If user inputs an object and it is a #model, point the Model property to it<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>else if(typeof(inputValue) == 'object' && inputValue.type == '#model){<br> <x-tab> </x-tab><x-tab> </x-tab>obj.Model = inputValue;<br> <x-tab> </x-tab><x-tab> </x-tab>return(obj);<br> <x-tab> </x-tab>}<br> <x-tab> </x-tab><br> <x-tab> </x-tab></font><font face="Courier New, Courier" color="#008000">// If we got this far, input is not a valid type<br> </font><font face="Courier New, Courier"><x-tab> </x-tab>app.LogMessage('CustomObject(Argument[0]) -- Invalid input', 2);<br> <x-tab> </x-tab>return(false); <br> }<br><br> <br> </font><blockquote type=cite class=cite cite="">I intend to roll an object as a utility library of functions (as suggested as one possible use in the blog entry). If I'm understanding the example, I think thats what GUITools is doing as well. Am I to understand that the object is instantiated every time the "GUITools" command is called? Or is it cache'ing it somewhere in global memory the first time and just passing the same instance on subsequent calls? Or is the instantiation so cheap, it doesn't matter that you instantiate it in every function you use it in? Is there a good place to toss an object where it will persist for the remainder of the given XSI session? Or is that foolhearty?</blockquote><br> I ran into some problems with this technique recently after stacking too many objects too deep. While it was never an issue in XSI 3.5 on linux, it seems that with the changes to command registration in XSI 4+ and on windows, one must be cautious about treating jscript too much like a real programming language.<br><br> An important aspect to know about jscript is that its object functionality is a bit of a hack. In C++, Java, and other compiled languages when you instantiate an object, you get a separate memory space and control over your construction and destruction. Inheritance and polymorphism is stable and reliable to many levels deep.<br><br> In jscript, the whole object functionality is a clever hack in the language, using arrays and string copies of your functions. Methods, for example, are simply pointers to your function stored as a string in an array. When you call the method, jscript runs an eval() on your function string, then runs the function. When you include an ancestor object into your current object, jscript loads in the file and does string concatenation at runtime.<br><br> What this means is that you should be really careful about nesting objects inside other objects too many levels deep. 2 or 3 levels is okay, but any more than that and you are risking a jscript meltdown at runtime. Test your stuff early and often, especially for performance in order to prevent unexpected problems. The moment you run into strange and unexplainable variable scope issues, you've gone too many levels deep and you'll have to explode some of your earliest objects.<br><br> Other scripting languages, such as Python and Php are built around this kind of object component construction, and don't suffer the same isses. However, I have heard some rumors about a compilable version of jscript, so perhaps the future for jscript and OOP may be better. <br><br> <br><br> <blockquote type=cite class=cite cite="">thanks,<br> -brad<br> ---<br> Unsubscribe? Mail Majordomo@(protected) with the following text in body:<br> unsubscribe xsi</blockquote></body> </html>
|
|