Hello I am stuck on a case I don't get
here is the code
function car(speed) {
this.speed = speed;
this.accelerate = function() {
this.speed += 1;
}
}
var oldcar = new car(1);
function test(cb){
cb();
}
test(oldcar.accelerate);
console.log(oldcar.speed);
// 1
test(function(){ oldcar.accelerate(); });
console.log(oldcar.speed);
// 2
On the first function call test(), I observe that the this in the oldcar.accelerate method is set to window.
On the second case, the this is correctly set to oldcar.
I don't understand how calling test() with oldcar.accelerate instead of function(){ oldcar.accelerate(); } make such a difference.
Can someone explain the behavior ? thanks !
Because when you pass a method as a callback you are passing only the method, not the object which it belongs to.
When this is used in without any given scope, it defaults to window (or the closest surrounding scope).
A correct way is to pass the entire object, or an anonymous function has has access to the entire object..
function test(cb){
cb();
}
test(function(){ oldcar.accelerate() });
Related
I was reading an article. It is related to javascript. Here, is a code sample from the article. Please look at below.
function customobject(){
this.value = 2;
}
customobject.prototype.inc = function(){
this.value++;
}
function changer(func){
func();
}
var o = new customobject();
alert(o.value);
o.inc();
alert(o.value);
changer(o.inc);
alert(o.value);
My question is Why is the value for "o.value" unchanged when changer(o.inc) is called ?
o.inc is just a reference to the anonymous function:
function() {
this.value++;
}
Whereas this is the scope in which the function is executed. So when you run changer(o.inc), this is actually pointing to the global, in browsers it's window. It doesn't necessarily have anything to do with o.
You could bind the scope like this:
function changer(func, scope){
func.call(scope); // or apply
}
changer(o.inc, o);
Or simply:
function changer(func){
func();
}
changer(o.inc.bind(o));
The problem is because Function does not save instance for objects. You have to pass the object as argument so call your function, like this.
function customobject(){
this.value = 2;
}
customobject.prototype.inc = function(){
this.value++;
}
function changer(o){
o.inc();
}
var o = new customobject();
console.log(o.value); //It has to print 2
o.inc(); //Increment to 3
console.log(o.value); //It has to print 3
changer(o); //Increment to 4
console.log(o.value); //It has to print 4
Preety straight forward question, though I can't find the answer anywhere
I tried these two ways:
setInterval(function(){object/*or this*/.method()},500)
and
setInterval('object/*or this*/.method()',500)
setInterval in fact expects a method as the first argument, though there is an alternative syntax where the first argument can be a string of code (not recommended by most)
If you're having issues with that code, it may have to do with the scope of 'this'
setInterval(function(){this.method()},500)
In the above code, 'this' will refer to the closure itself, and wouldn't be the same as 'this.method' occurring outside of that closure. For example, the following would work:
function MyClass() {
this.thingy = 'yep this is a thingy'
}
var myClass = new MyClass()
// Will log 'MyClass yep this is a thingy'
setInterval(function() { console.log('MyClass', myClass.thingy) }, 1000)
Whereas the following will not work (presuming instantiating the object and calling foo()):
function MyOtherClass() {
this.thingy = 'also a thingy'
}
// Will log 'MyOtherClass undefined'
MyOtherClass.prototype.foo = function() {
setInterval(function() { console.log('MyOtherClass', this.thingy) }, 1000)
}
The second example will work if we get around using 'this' within the closure (presuming instantiating the object and calling bar()):
MyOtherClass.prototype.bar = function() {
var that = this
setInterval(function() { console.log('MyOtherClass', that.thingy) }, 1000)
}
Also be sure that setInterval is being passed the name of a function:
setInterval(someFunction, 500)
rather than executing a function as an argument
setInterval(someFunction(), 500)
This last line of code is usually a mistake, unless someFunction() returns a function itself ;)
The difference between your 2 ways for passing a function to setInterval is whether you want to pass your function as refrence of just copy of it. Allow me to explain it by example:
-1 Referring(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(function () {
obj.testMethod()
}, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
when you run this code, the result 'll be execution of the modified version of testMethod. Because here you dont copy the function! Instead, you refer to it. So whenever function implementation is changed, the last modified version is executed.
-2 Copying(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(obj.testMethod, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
Here all you do is you are passing a copy of the last defined version of the function testMethod to setInterval. So whatever changes you do to testMethod, the result of setInterval will not be changed.
I have this following piece of code:
var stats = {
....,
checkExistance :
function(url){
var newUrl = url.substring(0, url.lastIndexOf("/")) + "/asyncCheckChartExistance";
var xhrObj = stats.getXhr();
var poolInterval = setInterval("poll()", 100);
function poll(){
xhrObj.open("GET", newUrl, true);
xhrObj.send(null);
xhrObj.onreadystatechange = function(){
if(xhrObj.readyState === 4 && xhrObj.status === 200){
if (xhrObj.responseText.length === true){
console.log("Exists!");
clearInterval(poolInterval);
} else {
console.log("Not Yet!");
}
}
}
}
},
}
I created the stats namespace. In this namespace I'm trying to create a function which polls the server every second. I should access this function this way: stats.checkExistance(myUrl).
However it seems that the setInterval function is not able to see the poll() function. I know that this is normal behavior taking in consideration that these are nested inside another function.
If I were to write this in the Global namespace there would be no problem but I'm interested to make this work in this kind of namespace. Any ideas? Thanks!
when you pass a string to setInterval, it runs in the global scope, by default, where poll would not be defined since it only exists in the scope of the checkExistance function.
To fix the issue, pass an anonymous function to setInterval instead:
var poolInterval = setInterval(function () {
poll();
}, 100);
Passing an anonymous function is usually the best idea as it allows you to write any javascript expressions/statements for the interval instead of just calling one function.
When you pass a string to setInterval, that string is interpreted as global code, and since poll is not a global function, a reference error is thrown.
However, you can pass a function reference instead of a string, and since the poll function is available in the scope in which the setInterval invocation is made, you can just write this:
var poolInterval = setInterval( poll, 100 );
var stat = {
say: function(name){
function doit(){
console.log(name);
}
setInterval(doit, 1000);
}
};
stat.say("hi");
A simple demo to show how. You will see "hi" every second.
I have two containers, each triggers some function, which has to pass the object reference.
The function has nested queue and setTimeout, and I need to pas an object reference through both of them, to execute it on the right object.
This is what I tried:
var pimg=parent.find('.prod_img'); // This is the object I actually need to pass;
pimg.children('img:last').queue((function(aaa){
return function() {
whatthe= (function (itema) {
return function() {
itema.children('img').each(function(){
alert(itema.attr('id'));
//alert used for debug.
$(this).stop(false,false).animate({opacity:0},400,function(){
});
});
}
})(aaa)
aaa.data('timeout',window.setTimeout("whatthe()", 400));
}
})(pimg)
);
Now what happens is that if I trigger this function on both of my objects quickly, it would alert the same ID, which suggests that it never passes the object reference.
Note, the pimg is the actual object, it is then called aaa in queue reference, and then itema in the setTimeout reference, but they all supossed to point the same object.
Any advices are appreciated. Thanks
Your code is hurting by brain and I don't have your HTML to actually test this, but If I understand your problem correctly, this should work:
var pimg = parent.find('.prod_img'); // This is the object I actually need to pass;
pimg.children('img:last').queue((function(aaa){
return function() {
var whatthe = (function (itema) {
return function() {
itema.children('img').each(function(){
alert(itema.attr('id'));
//alert used for debug.
$(this).stop(false,false).animate({opacity:0},400, function(){});
});
}
})(aaa)
aaa.data('timeout',window.setTimeout(whatthe, 400));
}
})(pimg));
I have written a checkers game model which has a Board object that holds a bunch of Piece objects. The Piece objects have an event (pieceMoved) which they fire each time they are moved.
I am using EaselJS to render this game model in a canvas element.
The first thing I do is create an easeljs shape object to graphically represent each piece.
I wish to handle the pieceMoved event on the pieces' corresponding shape objects so that I can update the shapes location.
The code where I create the shapes and assign the event handlers looks something like this -
for (x=0;x<=(board_.getColCount()-1);x++) {
for (y=0;y<=(board_.getRowCount()-1);y++) {
var piece = board_.getPieceAt(x, y);
if (!piece) continue;
var circ = new Shape();
circ.eventHandler = function(pieceObj) {
this.x = pieceObj.x*squareSize;
this.y = pieceObj.y*squareSize;
}
piece.addEventHandler(circ.eventHandler);
....
The problem is that the 'this' keyword inside the event handler doesn't refer to the right thing. I also tried the following -
piece.addEventHandler(function(px) { return circ.eventHandler; });
But again, 'this' refers to the wrong object.
What is the proper way to do something like this?
In an event handler, this refers to the object which fired the event. There are ways around it, but my philosophy is that you should embrace the convention rather than fight against it. So, just separate the method from the event handler:
piece.addEventHandler (function (e) {
circ.eventHandler(e);
});
In your case, since it looks like you have looping issues, create a handler factory:
function getHandler (circ) {
return function(e) {
circ.eventHandler(e);
};
}
for (var i = 0; i < circles.length; i++) {
piece.addEventHandler(getHandler(circles[i]));
}
Edit: Prettify it with Array.forEach(). I'm guessing that circles is not an Array, so you'll probably have to use.call():
[].forEach.call(circles, function(circ) {
piece.addEventHandler(function(e) {
circ.eventHandler(e);
});
});
Try moving the code to create the circle shape to a method such as,
function addShape(piece) {
var circ = new Shape();
piece.addEventHandler(function (pieceObj) {
circ.x = pieceObj.x * squareSize;
circ.y = pieceObj.y * squareSize;
});
return circ;
}
This will make the loop look like,
for (x = 0; x <= (board_.getColCount() - 1); x++) {
for (y = 0; y <= (board_.getRowCount() - 1); y++) {
var piece = board_.getPieceAt(x, y);
if (!piece) continue;
var circ = addShape(piece);
...
}
}
I realize that this doesn't actuall use this but, in this case, it make the code more complicate and you really don't need it.
piece.addEventHandler(function(px) {
circ.eventHandler(px);
});
Functions lose their context when you pass them around directly. This is because it's the dot syntax of the invocation that adds the context. So when you simply pass a reference to that function, it's invoked directly and you lose context.
So for callbacks that depend on context, it usually wise to pass an anonymous function as the callback and then do whatever you need to do inside of that.
Alternatively, if you want to edit how the callback is called you could do this:
// event happened invoke handler!
myEventHandler.call(this)
Which will execute the function object with the context of where you are calling it from (like say, your Piece isntance).
Try using bind (no arguments):
piece.addEventHandler(circ.eventHandler.bind(circ));
or use an anonymous function, since this is a closure problem:
piece.addEventHandler((function(circ) {
return function() {
circ.eventHandler.apply(circ, arguments);
};
})(circ));
You must simply make a closure:
var that = this;
circ.eventHandler = function(pieceObj) {
that.x = pieceObj.x*squareSize; // we will "remember" the correct scope now!
that.y = pieceObj.y*squareSize;
}
Since the function fires in a different scope than the scope it was created in, it gets confused, in a way. You need to set it straight by closing over the variable. You do this by making a reference to it outside of your function and then using that reference inside your function. This will allow the function to always use the correct "this".