d13 Posted April 12, 2014 Share Posted April 12, 2014 Hello smart people! I have a programming puzzle/question/observation that I'd love some comments on. Here's a "class" that takes a function called setup as an argument. The quirky part is that setup references the object that the class is creating. function Class(setup) { this.test = "Hello!"; this.start = setup;}var instance = new Class(setup);instance.start();function setup() { console.log(instance.test);} This displays: "Hello!", as you would expect. You can see that the the class creates a reference to the setup function called start. When an instance of the class is created, the instance calls the start method. The method displays the test property on the instance. So that's all cool. But ... ! What if I want the instance to run the start method automatically when it's created?I can try calling the start method inside the class's constructor, like this: function Class(setup) { this.test = "Hello!"; this.start = setup; this.start(); //<= New!}var instance = new Class(setup);function setup() { console.log(instance.test);} Now when it runs, it throws an error saying instance is undefined. I know why: because the instance is being invoked before it's been created. Here's my question: Does anyone know if it's possible to wait until an object is instantiated, and then automatically run a method that references that same object? Here's a JSBin for anyone who wants to test this out: http://jsbin.com/juniv/1/edit?js,console Quote Link to comment Share on other sites More sharing options...
OadT Posted April 12, 2014 Share Posted April 12, 2014 <edit> It's possible per setTimeout but it's a dirty hack, so it's just for demonstration to show that the start function will work later</edit>function Class(setup) { this.test = "Hello!"; this.start = setup; (function(that){ window.setTimeout(function(){that.start()},1) })(this)}var instance = new Class(setup);//instance.start();function setup() { console.log(instance.test);}Solution: create an object inside the function:function Class(setup) { var obj = {} obj.test = "Hello!"; obj.start = setup; obj.start(); //<= New! return obj}var instance = new Class(setup);function setup() { console.log(this.test);} But here you need to write your setup function with this. Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 12, 2014 Share Posted April 12, 2014 var Class= function(setup){ this.test = "hello"; this.start = setup || null; if (this.start) this.start();}var setup = function(){ console.log(this.test);}var instance = new Class(setup);Here is how I would do what your trying to do. Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 12, 2014 Share Posted April 12, 2014 Allow me to explain. When you create your Class function this.start couldn't possibly have a value since setup doesn't exist yet, so that would fail. Also if you ever created a Class without passing it a value it would fail, so doing the check on creation is the safe thing to do. Also not that it mattered in your example, but in the future you would probably want to use a reference to this.test in the setup function instead of instance.test, so you could have multiple classes, multiple this.test values, but only need one setup function to display the information. Hope this helps. Quote Link to comment Share on other sites More sharing options...
d13 Posted April 13, 2014 Author Share Posted April 13, 2014 Thanks so much.. here are my observations! > window.setTimeout(function(){that.start()},1) It works! It gives the instance just enough time, 1 millisecond, to instantiate the object before firing the start method. > obj.start(); I really like this solution, it seems robust and sensible. > if (this.start) this.start(); Wouldn't this.start always remain undefined when the constructor runs?Ok, I read your post below, I've got it now!The main point is that if "instance" is changed to "this" in the setup function the scope is correct.I think that's the biggest lesson in all this, so thanks for pointing it out! > When you create your Class function this.start couldn't possibly have a value since setup doesn't exist yet I believe that function declarations are evaluated at compile time before any other code tuns, so setup should already exist at the time that the class is instantiated. I tested this by using a different version of setup that doesn't reference instance, and it works: function setup() {console.log("It works!"}> you would probably want to use a reference to this.test in the setup function Yes, that makes a lot of sense! Thanks so much for your insights, ericbasti and Oatd, I'm really understanding this problem much better now. Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 13, 2014 Share Posted April 13, 2014 You do realize that you have zero reason to use a timeout for this. Just doing this :function Class(setup) { this.test = "Hello!"; this.start = setup; this.start();}var instance = new Class(setup);//instance.start();function setup() { console.log(this.test);}works. This avoids all of my little checks to make sure something is passed in (which you should be doing anyways, if your going to try running it on creation). By simply changing it to 'this.test' instead of 'instance.test' allows this to work. Anytime I see a timeout being used like this I cringe and have to say something, its a hack, not a solution. Eventually you'll run into a situation or a browser that takes longer than 1ms, and you'll have to make it 100ms, then 1000ms... Quote Link to comment Share on other sites More sharing options...
OadT Posted April 13, 2014 Share Posted April 13, 2014 Yep, srry for the confusion.I didn't mean setTimeout a real solution, I just wanted to show it as kind of "waiting" until theres nothing to do. It of course just works in cases where the function didn't effect the following code. I will edit my post to make it clear. Again srry Quote Link to comment Share on other sites More sharing options...
Thigydaze Posted April 13, 2014 Share Posted April 13, 2014 What ericjbasti says is true about the timeout, plus it feels clunky. If setup is purely used by the any instance of "Class", I'd put it inside as a prototype, so that it feels more OO. function Class(name) { this.test = "Hello! "+name; this.setup();} Class.prototype = { setup : function() { console.log(this.test); }} var instance = new Class("one");var instance2 = new Class("two"); However, if setup is used elsewhere, then it has to stay outside of Class. Quote Link to comment Share on other sites More sharing options...
Gio Posted April 13, 2014 Share Posted April 13, 2014 > window.setTimeout(function(){that.start()},1) It works! It gives the instance just enough time, 1 millisecond, to instantiate the object before firing the start method. Without saying that one method is better than another, I just wanted to note that even setTimeout with a delay of 0 milliseconds would have worked there, and there are no possible timing issues. All you want to do is execute a piece of code after the rest has been executed and an idle state has been reached, which is what a setTimeout(..., 0) would do. I'm not sure whether it's a hack in this particular case, but in lots of situations it's a perfectly legitimate solution Quote Link to comment Share on other sites More sharing options...
ericjbasti Posted April 13, 2014 Share Posted April 13, 2014 Yeah, when I said it was a hack, it wasn't because it was setTimeout, it was because of how setTimeout was being used to do something that should never require a setTimeout. The issue wasn't with timing, the issue in this case was understanding how the scope and order of operations work. The script didn't fail because the function couldn't run, it failed because instance couldn't possibly exist before setup, yet setup requires instance to exists (thus the error saying instance is undefined). Quote Link to comment Share on other sites More sharing options...
d13 Posted April 14, 2014 Author Share Posted April 14, 2014 Thanks everyone! > setTimeout Yes, a kind-of-hack, but cool because we can learn from it.It's science > this.start() ... this.test(); This clarifies the scope, which I think is the most important key to understanding this problem. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.