I did some animation using var oDiv = {}; oDiv.x = 100; oDiv.y = 100; oDiv.node = document.getElementById('divImage'); oDiv.node.style.position = "absolute"; oDiv.doAnimation = function() { oDiv.x += 1; oDiv.y += 1; oDiv.node.style.top = oDiv.y + 'px'; oDiv.node.style.left = oDiv.x + 'px'; setTimeout(oDiv.doAnimation, 33); } Code (markup): and it works fine. When it is changed to the traditional class definition, then it fails: function Animation() { this.x = ... etc... this.doAnimation = function() { // do something // set delay setTimeout(this.doAnimation, 33); } } oDiv = new Animation(); oDiv.doAnimation(); Code (markup): which will fail. The second time when the doAnimation() is called, it seems that it doesn't have a concept of this.x, etc. It is the same if I use the more popular way of defining a class by moving the function out: function Animation() { this.x = ... etc... } Animation.prototype.doAnimation = function() { // do something } Code (markup): and it is the same, the second time when doAnimation() is invoked by the setTimeout, it doesn't have a concept of this.x. Does someone know what it is? hm... I don't need to use prototype.js and use something like doAnimation.bind(this) ? Can it be a self-contained script that doesn't rely on other framework? Thanks.
var foo = { init: function(params) { options = params; setTimeout(this.bar, 1000, this); // you need to bind this to the function as it will run outside the }, bar: function() { alert("bar " + this.options.foobar); } } foo.init({foobar: "soap"}); // alert bar soap PHP: this will work in FF2/3 but it will fail in IE6 as it's implementation does not allow you to bind... Works in IE7 anyway, no IE6 here to test. frameworks can fix this, even if you are trying to be standalone. in mootools you'd do... (function() { this.method(); }).delay(1000, this); it also has something.bind(this); as a convenient syntax. as for your problem in IE6, you can do var _this = this; (as a global) and then _this.doAnim... you REALLY could bebefit from mootools and moo.fx if you are serious in your animation .... it has all sorts of morths, transitions, css based and other, chaining of events and so forth. oh, its also crossbrowser compliant and very pleasant to write with - really extends JS to a new level. if you want standalone... thats gonna be hard EDIT: my bad, just saw you use prototype - surely it has the bind here fixed? i noticed in vanilla js, on the first iteration of the function in setTimeout, with the bind set to this, this is the object. after the first recursive call to self however - this becomes "window". that's crap...
thanks for your post. that's right... i tried using prototype.js with this.doAnimation.bind(this) and it works! The only thing is instead of using the whole prototype.js, i would like to just use this bind() function instead... to save file size. So it looks like a common pattern that, if we want any callback or the function in setTimeout() to actually call the function ON this object, we need to use foo.bind(this) instead of foo. by the way, you know you can get a Virtual PC for free to test in IE 6. It is totally free from Microsoft. details at: http://www.sitepoint.com/forums/showthread.php?p=3924905
function createBoundedWrapper(object, method) { return function() { return method.apply(object, arguments); }; } Code (markup): From a good article on binding: http://alistapart.com/articles/getoutbindingsituations Could maybe do something like : setTimeout(createBoundedWrapper(this, this.mymethod), 1000); Code (markup):
wow, that really works. I wonder you need to do a return method.apply(object, arguments); but cannot do something like object.method() ? the other method i tried was to have var me = this; either at the very beginning of the contructor, and have those functions defined in the contructor to use setTimeout(function() { me.doAnimation() }, 33); or, if defining the function outside the contructor as Animation.prototype.doAnimation() { var me = this; // ... some code setTimeout(function() { me.doAnimation() }, 33); } Code (markup): and they will work, but it is extra effort to add those var me = this; definitions. by the way, so we need to only use this technique when it is any call back, such as by setTimeout, or a callback that is invoked when the Ajax is finished (by XHR or by Prototype.js's Ajax class)?
Because apply() binds the 'this' variable to whatever you want. I'm not sure why the JS implementations or spec chooses to throw away bindings. Maybe just because once you use a reference then it becomes hard or impossible to decide what object it should actually be bound to, unless you tell it explicitly with apply() or call(). From the article: This is the way I usually do it. I just assign 'this' to a local variable at the top of the enclosing method or whatever. It's not pretty and not clever but it works fine. Anytime you want to control what the 'this' variable will be in the callback. Sometimes it doesn't matter, sometimes you don't eve reference 'this' in the callback. YUI has a "scope" parameter (http://developer.yahoo.com/yui/connection/#scope) in the callback object that specifies what object to execute in the scope in, or in other words, what 'this' will be. Mootools and the other popular libraries/frameworks probably have a similar mechanism. You should take dimitar's advice and use a framework whenever possible.