brain fail - returning custom instance from a constructor

For AI and campaign script related discussions and questions
Post Reply
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

brain fail - returning custom instance from a constructor

Post by aubergine »

Hi,

My brain is hurting trying to do something which should be simple but, either due to a mistake on my part or something in the WZ JS environment, it is going horribly wrong.

Let's say I define a class:

Code: Select all

var Foo = function() {
  // code to run on instantiation
  // this == newly created object instance
  this.fish = "haddock";
}

Foo.prototype.addTask = function() {
  // code
}
I can instantiate a Foo like so:

Code: Select all

var bar = new Foo();

bar.fish; // haddock

bar.addTask(); // runs addTask
Above, bar will be a javascript object, and it's proto chain will have an .addTask method in it.

When instantiation happens, if I understand correclty, the following sequence happens:

* a new object is created, that inherits from constructor prototype (Foo.prototype in examples above)
* the constructor function is run, with "this" pointing at the new object (so code in Foo() is run, but scoped to the new object instead of Foo).
* unless the constructor function (Foo()) returns something, the new object is returned as the result of the whole "new" expression.

That's why bar is a new object, that inherits from Foo.prototype thus has an ".addTask()" method, and has an instance-level fish property with value "haddock".

So far so good. All easy stuff.

But what if I want new Foo() to return a function, instead of an object.

According to everything I know about ecmascript and javascript, I should just be able to do this:

Code: Select all

var Foo = function() {
  var returnVal = function() {
     // some code
  }

  returnVal.prototype = this.prototype; // where 'this' is the newly created object that would usually be returned
  // I've also tried returnVal.prototype = Foo.prototype but results are the same either way :s

  returnVal.fish = "haddock";

  return returnVal;
} // scroll down this code block....

var bar = new Foo();

bar; // should be a function - and it is! good!

bar.addTask; // should be a function (proto inheritance) -- but it's not there! bad!

bar.fish; // should be a haddock - and it is! good!
So I've successfully returned a function from the instantiation process, and it even has the desired instance-level properties defined on it.

But, for some reason which is totally eluding me, the proto chain is not working properly. Where is the .addTask() method gone?

I know I can just cheat and copy across all the Foo.protoytype methods, etc., but that's crufty and wasteful. I just want to plonk Foo's prototype on to returnVal because it's quicker and nicer.

But it's not working!!! AARRGGHHH!!! It's doing my nut in.

Can anyone see any obvious mistakes I'm making?
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
Duha
Trained
Trained
Posts: 287
Joined: 25 Mar 2012, 20:05
Location: SPb, Russia

Re: brain fail - returning custom instance from a constructo

Post by Duha »

My brain is hurting too.

you use constructor in unusual way. Can you describe what you want?

"this" is blank object, this.prototype is undefined use Foo.prototype

prototype is added to instances bar is not instance new bar() is instance.
http://addons.wz2100.net/ developer
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

Well, if normal instance is an object that inherits from constructor class, what I want is the same but instead of getting an object back I want a function back.

As mentioned in the last code block, I already tried setting it to Foo.prototype, same result. :s

The problem appears to be that when you call a method on a function, it doesn't appear to be looking on it's prototype chain for that method, at least not the first level of its proto chain anyway.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
Duha
Trained
Trained
Posts: 287
Joined: 25 Mar 2012, 20:05
Location: SPb, Russia

Re: brain fail - returning custom instance from a constructo

Post by Duha »

aubergine wrote:Well, if normal instance is an object that inherits from constructor class, what I want is the same but instead of getting an object back I want a function back.
Please don`t use constructor for it. You can use just any other function.

Code: Select all

function factory() {
  var something =  function() {...};
  return something;
}

As mentioned in the last code block, I already tried setting it to Foo.prototype, same result. :s
The problem appears to be that when you call a method on a function, it doesn't appear to be looking on it's prototype chain for that method, at least not the first level of its proto chain anyway.[/quote]

Try both Foo.prototype and new bar()

or just copy attributes from one obj to another.
http://addons.wz2100.net/ developer
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

I'm copying proto properties on to the new object for now, it's not ideal but it works. Luckily it only gets used when processes are created so it's not causing any performance issues.
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

Ah, found the problem - all Function instances inherit from Function.prototype ONLY. *sigh*

So, myFunc.prototype <-- the prototype is basically ignored unless the function is a constructor and even then the prototype is only of use if it's shoved on to a non-function object. Grr.

Life would be so much easier if I could create a function at runtime (which is do-able) but give it a name. So far I've only been able to create anonymous functions (where function.name == null).

...although, if I can define the code to go inside the function I can just put the name in that code... hrm...

*back to coding*
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

Grrr, no, that won't work either, because fn.name is read only and non-configurable...

Why are Function instances such a PITA to work with in JS?!! :evil:
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
Duha
Trained
Trained
Posts: 287
Joined: 25 Mar 2012, 20:05
Location: SPb, Russia

Re: brain fail - returning custom instance from a constructo

Post by Duha »

aubergine wrote:Ah, found the problem - all Function instances inherit from Function.prototype ONLY. *sigh*

So, myFunc.prototype <-- the prototype is basically ignored unless the function is a constructor and even then the prototype is only of use if it's shoved on to a non-function object. Grr.

Life would be so much easier if I could create a function at runtime (which is do-able) but give it a name. So far I've only been able to create anonymous functions (where function.name == null).
...although, if I can define the code to go inside the function I can just put the name in that code... hrm...

*back to coding*
Whats wrong with anonymous functions? What are you doing with "name".

What type of object do you want to create in runtime: function(simple function) or function(function with constructor to make instances)
http://addons.wz2100.net/ developer
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

The reason I want a name property is so that I can use a sort of "introspection" within the script.

For example, when a "Process" is running a task, I can make an "alert()" function available to the task that, when called from within the task, can determine:

* the process that the task is running in (and via that access the process properties)
* the name of the task
* the task's arguments and properties

This way, my alert() function adapts itself to the task being run, despite the task being defined externally to the process API.

I'd like to increase the use of such techniques because they make the external code, and often internal code, quicker and easier to develop and also more concise and transparent as to the meaning of that code.

Currently when a process is instantiated, I have it returning a new function instance that has a bunch of instance-level properties. This way the new process can be run directly from it's instance (which is a function) rather than needing to call a method on it (if it were just a basic object). This means that I can use the new process as a named global property and then invoke it using timers, etc. And, because it's on the global scope, the process becomes accessible from all functions in my script, so I can easily run the process as if it were a global function (which, really, it now is) and pass in "run parameters" that contain data I want the process to, erm..., process.

So, regarding wanting the process function to be named... If it's named, I can internally reference the process function from within the process function, I can use introspection to do things within the process and also adapt external functions to the process, etc.

Sadly, functions can only be named via a function declaration (statement) or expression, neither of which allow the name to be specified via a variable. The function constructor doesn't allow name to be specified, and even if it did creating functions via the function constructor yields poorly optimised functions (although functions defined within the function body will be optimised normally, which I always find strange).

I could possibly use eval(), as these functions only get defined once at the start of a script (or rarely thereafter) so the performance hit would be practically non-existent. But I so rarely use eval() that I have no idea if functions created within it would enclose their parent scope (eg. be able to retain access to the variables and arguments of the function in which eval() was run) so more reading is required on that matter...
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
User avatar
Duha
Trained
Trained
Posts: 287
Joined: 25 Mar 2012, 20:05
Location: SPb, Russia

Re: brain fail - returning custom instance from a constructo

Post by Duha »

aubergine wrote:The reason I want a name property is so that I can use a sort of "introspection" within the script.

For example, when a "Process" is running a task, I can make an "alert()" function available to the task that, when called from within the task, can determine:

* the process that the task is running in (and via that access the process properties)
* the name of the task
* the task's arguments and properties

This way, my alert() function adapts itself to the task being run, despite the task being defined externally to the process API.

I'd like to increase the use of such techniques because they make the external code, and often internal code, quicker and easier to develop and also more concise and transparent as to the meaning of that code.


Currently when a process is instantiated, I have it returning a new function instance that has a bunch of instance-level properties. This way the new process can be run directly from it's instance (which is a function) rather than needing to call a method on it (if it were just a basic object).
You ruin concstructor idea just for save some chars?

Code: Select all

var foo = new Process();
//vs
var foo = Process.run();
You need name? Use some hack:

Code: Select all

var foo = function() {}
foo._name = name
function getTaskName(funct) {return funct.name || funct._name;}
This means that I can use the new process as a named global property and then invoke it using timers, etc. And, because it's on the global scope, the process becomes accessible from all functions in my script, so I can easily run the process as if it were a global function (which, really, it now is) and pass in "run parameters" that contain data I want the process to, erm..., process.

Code: Select all

foo = function() {}; # with out var it is global
I don`t get global idea, do you have some code example of process usage?
http://addons.wz2100.net/ developer
User avatar
aubergine
Professional
Professional
Posts: 3459
Joined: 10 Oct 2010, 00:58
Contact:

Re: brain fail - returning custom instance from a constructo

Post by aubergine »

I'll be releasing the API early next week for public beta testing / criticism (:
"Dedicated to discovering Warzone artefacts, and sharing them freely for the benefit of the community."
-- https://warzone.atlassian.net/wiki/display/GO
Post Reply