What is the best way to fire a method in many children by calling the parent's method?
For example, lets say I have a parent object Foo which has many instances: BarX, BarY, etc.
Foo = function(){
x = null;
y = null;
move = function(){
x += 1;
y += 1;
};
}
BarX = new Foo();
BarX.x = 50;
BarX.y = 50;
BarY = new Foo();
BarY.x = 200;
BarY.y = 200;
Is there any easy way to fire off the move function in all instances? Am I limited to looping through the instances and firing off the function like that or can I somehow fire the function in Foo and have it trickle down and fire off all instances who extend Foo?
No. But you could be more clever about it. Make a static moveAll function on Foo. Examples make things clearer. Here is the fiddle.
var Foo = function(x, y){
this.x = x;
this.y = y;
this.move = function(){
x += 1;
y += 1;
alert(x + ' ' + ' ' + y);
};
Foo.instances.push(this); // add the instance to Foo collection on init
};
Foo.instances = [];
Foo.moveAll = function(){
for(var i = 0; i < Foo.instances.length; i++)
Foo.instances[i].move();
}
var a = new Foo(5, 6);
var b = new Foo(3, 4);
Foo.moveAll();
Related
Making subclases is easy. I just follow this structure:
var Car = function(x){
this.x = x;
this.y = 10;
};
Car.prototype.say_position = function(){
console.log("("+this.x+", "+this.y+")");
}
var a = new Car(2);
a.say_position(); //(2, 10)
Using prototype for classes is good for performance as every instance doesn't have the method repeated. For making subclasses I followed the convention explained here: https://www.udacity.com/course/object-oriented-javascript--ud015
which is as follows:
var Car = function(x){
this.x = x;
this.y = 10;
};
var Van = function(x){
Car.apply(this, arguments);
};
Van.prototype = Object.create(Car);
Van.prototype.constructor = Car;
Meanwhile, when I try to use prototyped methods with this structure...
var Car = function(x){
this.x = x;
this.y = 10;
};
var Van = function(x){
Car.apply(this, arguments);
};
Van.prototype = Object.create(Car);
Van.prototype.constructor = Car;
Car.prototype.say_position = function(){
console.log("("+this.x+", "+this.y+")");
}
var car = new Car(2);
car.say_position(); //(2, 10)
var van = new Van(2);
van.say_position(); //Error!
As you can see, when calling say_position() on van, it throws an error. Shouldn't Van's prototype delegate to Car's prototype and find that function there? Can anyone explain and solve this?
The issue that you're having is that the argument to Object.create should be Car.prototype
Here's working code
var Car = function(x){
this.x = x;
this.y = 10;
};
var Van = function(x){
Car.apply(this, arguments);
};
Van.prototype = Object.create(Car.prototype);
Van.prototype.constructor = Car;
Car.prototype.say_position = function(){
console.log("("+this.x+", "+this.y+")");
}
var car = new Car(2);
car.say_position(); //(2, 10)
var van = new Van(2);
van.say_position(); //(2, 10)
The Mozilla docs are always a great reference for these types of issues
Sorry for the newb question here, but Im new to javascript. Ideally I would like to call for myLoop(latLong); but unless I make the variables outside of the function, I can't seem to have .setPosition() recognize the variable.
var x = 0;
var y = 0;
var z = 0;
var v = 0;
function xy(a,b,c,d) {
var longDistance = Math.abs(a-d);
var longTime = longDistance/0.1*0.5;
var latDistance = b-c;
var latRate = latDistance/longTime*0.5;
x = a; //origin long
y = b; //oringin lat
z = latRate;
w = d; //destination long
v = c; //destination lat
}
function myLoop () {
setTimeout(function () {
var latLong = new google.maps.LatLng(y,x);
marker.setPosition(latLong);
x = x + 0.1;
y = y - z;
if (x < w && y < v) {
myLoop();
} else {
alert('finished');
}
}, 0.5)
}
xy(-118,33,40,-73);
myLoop();
You simply need to pass the latLong variable into the myLoop() function recursively.
To do this, you can create your first latLong variable outside of the function, then call the function (passing in the first latLong variable), then within the latLong function, check for your conditions, and if you need to call the myLoop function again, update the latLong variable and then call the myLoop function again.
Here is what your recursive code would look like:
var x = 0;
var y = 0;
var z = 0;
var v = 0;
// Your first latLong
var latLong = new google.maps.LatLng(y,x);
function xy(a,b,c,d) {
// ...
}
// Pass in the latLong variable
function myLoop (latLong) {
setTimeout(function () {
marker.setPosition(latLong);
x = x + 0.1;
y = y - z;
if (x < w && y < v) {
// now create a new latLong, and pass it
// back into this function recursively
latLong = new google.maps.LatLng(y,x);
myLoop(latLong);
} else {
alert('finished');
}
}, 0.5)
}
xy(-118,33,40,-73);
// Now call the myLoop function to get the recursion started
myLoop(latLong);
Alternatively, you can wrap all the code up into one function
Using the revealing module pattern, you can wrap up all your loop functionality in one place (within a function object called latLongGenerator), allowing for a nice separation in your code logic, but still giving you a clean interface to use. The restructured "revealing module" code would look like this:
var latLongGenerator = (function () {
var x = 0;
var y = 0;
var z = 0;
var v = 0;
var latLong;
function setXY(a,b,c,d) {
var longDistance = Math.abs(a-d);
var longTime = longDistance/0.1*0.5;
var latDistance = b-c;
var latRate = latDistance/longTime*0.5;
x = a; //origin long
y = b; //oringin lat
z = latRate;
w = d; //destination long
v = c; //destination lat
// pass in the initial latLong to myLoop(latLong) from here
latLong = new google.maps.LatLng(y,x);
myLoop(latLong);
}
// This is the only function that will
// be exposed publicly on this function
// Example usage: latLongGenerator.startLoopWith(0,0,0,0);
function startLoopWith(a,b,c,d){
setXY(a,b,c,d);
}
function myLoop (latLong) {
setTimeout(function () {
marker.setPosition(latLong);
x = x + 0.1;
y = y - z;
if (x < w && y < v) {
// recursively call the loop from here
latLong = new google.maps.LatLng(y,x);
myLoop(latLong);
} else {
alert('finished');
}
}, 0.5);
}
return {
startLoopWith:startLoopWith
};
})();
// Finally, you can start your loop by passing in
// your initial values to latLongGenerator.startLoopWith(...)
latLongGenerator.startLoopWith(-118,33,40,-73);
This structure gives you a clean way of encapsulating all your calculation logic, while also giving you a nice, clean entry point. Using this new refactor, you can get your loop started with one line:
latLongGenerator.startLoopWith(-118,33,40,-73);
I haven't tested this code, but it should help you get on the right track.
Hope this helps!
In Mootools the following pattern occurs frequently:
var x = this.x = function(){}
For example:
var typeOf = this.typeOf = function(item){ ...
I understand multiple assignment results in function being assigned to both x and this.x. But I thought in the global scope x is implicitly this.x, so it seems redundant. Is this an optimization technique, or is there some other purpose to this pattern?
This is only redundant if this code isn't executed in a function.
If it's in a function, the var is local, even if the context (this) is the global one.
Look at this :
function a() {
var x = 1;
console.log(x)
}
function b() {
console.log(x); // fails
}
a();
b();
If you want to be both able to use x directly in a and have this.x in b, then you need the double assignation :
var x = this.x = 1;
I frequently use this pattern in big functions when I have a variable I use a lot and for which I prefer a direct access without this..
var x is not equals this.x, var x is a varible private of a js class and this.x is a public propertie, so the code create 2 ways for invoke a function
Here an example:
function exampleA() {
this.x = 1;
var x = 2;
this.show = function () {
alert("A.x:" + x);
alert("A.this.x:" + this.x);
};
}
function exampleB() {
var x = this.x = 1;
this.x +=1;
this.show = function () {
alert("B.x:" + x);
alert("B.this.x:" + this.x);
};
}
$(document).ready(
function () {
var example1 = new exampleA();
example1.show();
var example1 = new exampleB();
example1.show();
});
Consider this errorful code:
x = {
y : "why",
z : function() {
return y + " zed";
}
}
The function z does not work: "ReferenceError: y is not defined".
Is there a way to access y from within the function z without fully specifying it as x.y?
I could of course rewrite this as
x = function() {
var self = this;
this.y = "why";
this.z = function() {
return self.y + " zed";
};
return this;
}();
... but gosh.
Simply use this if you call the function with x.z():
var x = {
y : "why",
z : function() {
return this.y + " zed";
}
};
DEMO: http://jsfiddle.net/hZxVu/
No, you will need to rewrite it as such. y is a property of that object, and you can't access it without the object - unlike accessing a variable (e.g. self in your rewrite) from a closure.
Of course, when you invoke the function as x.z(), the this keyword will point to the object too, so that you can write it as
return this.y + " zed";
as long as you always call the function in context of that object.
#VisioN has the straight-forward answer. It might help to visualize why this is necessary if you rewrite your code as such:
var x = {};
x.y = "why";
x.z = function() {return this.y + " zed"; };
alert(x.z());
Here y and z are properties of an object, but there is no functional closure scoping. You need the "this" keyword to access a property of the parent object.
Alternatively,
var x = function () {
var y = "why";
var z = function () { return y + " zed?"; };
return z();
};
alert(x());
This demonstrates functional scoping by accessing y without using this. Inside of x, y is known. Outside, it is not.
Using the revealing module pattern:
var x = (function () {
y = "why";
z = function() {
return y + " zed";
};
return {
"y": y,
"z": z
};
})();
//now you can call:
x.y // "why"
x.z() // "why zed"
I am having issues accessing public variables from an object in a prototyped function... from what I have read, this should work, but perhaps someone with a more experienced eye can point out what I am doing wrong.
In this, I am trying to write my own animation object, with self deletion that another manager can use. callback is a command written as a string, x&y are starting position, x2,&y2 are ending position, time is time in seconds, and element is the element that moves. doTime is the result of a benchmark function that executes on page load to correctly set the timeOut offset.
I can create a working version with inside functions, but as this object is created several times, I was wanting to prototype the function to improve creation speed.
Test show that none of the this.vars are reading within the prototype function.
function circDelta(progress) {
if (progress < .5)
return (1 - Math.sin(Math.acos(progress))) / 2
else
return (2 - (1 - Math.sin(Math.acos(2*(1-progress))))) / 2
}
function animation(element,x,y,x2,y2,time,callback){
this.e = element;
this.x = x;
this.y = y;
this.x2 = x2;
this.y2 = y2;
this.t = time * 1000;
this.c = callback;
this.cT = 0;
this.id = setTimeout(this.frame, doTime);
}
animation.prototype.frame = function(){
this.cT+=doTime;
if(this.cT>=this.t)
{
this.e.style.left = this.x2+'px';
this.e.style.top = this.y2+'px';
if(typeof this.c === 'function')
this.c();
}
else
{
this.e.style.left = ((this.x2-this.x)*circDelta(this.t/this.cT))+this.x+'px';
this.e.style.top = ((this.y2-this.y)*circDelta(this.t/this.cT))+this.y+'px';
this.id = setTimeout(this.frame, doTime);
}
};
I am using the function like this:
this.curr_anim = new animation(hC.menus[0],0,0,0,30,1.5,hC.anim_finished);
any help would be greatly appreciated.... Thank you in advance.
you will probably having scoping issues in setInterval call,
because setInterval will be called after specified interval and with the scope of window object so it will not be able to access object data using this,
so you need to pass reference of object using a local variable, you can achive this by use of closures in setInterval like this
var me = this;
this.id = setTimeout(function() {
me.frame();
}, doTime);
so your final code will look like this
function circDelta(progress) {
if (progress < .5)
return (1 - Math.sin(Math.acos(progress))) / 2
else
return (2 - (1 - Math.sin(Math.acos(2*(1-progress))))) / 2
}
function animation(element,x,y,x2,y2,time,callback){
this.e = element;
this.x = x;
this.y = y;
this.x2 = x2;
this.y2 = y2;
this.t = time * 1000;
this.c = callback;
this.cT = 0;
var me = this;
this.id = setTimeout(function() {
me.frame();
}, doTime);
}
animation.prototype.frame = function(){
this.cT+=doTime;
if(this.cT>=this.t)
{
this.e.style.left = this.x2+'px';
this.e.style.top = this.y2+'px';
if(typeof this.c === 'function')
this.c();
}
else
{
this.e.style.left = ((this.x2-this.x)*circDelta(this.t/this.cT))+this.x+'px';
this.e.style.top = ((this.y2-this.y)*circDelta(this.t/this.cT))+this.y+'px';
var me = this;
this.id = setTimeout(function() {
me.frame();
}, doTime);
}
};