css element and setInterval error - javascript

I get this error:
cannot read property of 'css' undefined
and don't know how to fix it!
Here is the code:
//constructor for Car objects
var Car = function(x, y){
this.x = x;
this.y = y;
};
//adding the draw method to Car objects
Car.prototype.draw = function(){
//obtaining the image that the car will have
var carHtml = '<img src = "http://nostarch.com/images/car.png">';
// attaching the chosen image file to the current object being drawn
this.carElement = $(carHtml);
//giving the image of the Car object its (x, y) position on the plane using css
this.carElement.css({
position: "absolute",
left: this.x,
top: this.y
});
//attaching the image associated with the Car object to the body of the html document (the web page)
$("body").append(this.carElement);
};
//adding the moveRight method to Car objects
Car.prototype.moveRight = function(){
this.x = this.x + 50;
this.carElement.css({
left: this.x,
top: this.y
});
};
var tdi = new Car(100, 200); //creating a new instance of the Car class
tdi.draw(); //invoking the draw method on an object so it appears on the screen
var move = tdi.moveRight;
var run = setInterval(move, 30);
Any help? Severely lacking in understanding here...

This issue is cause, this does not refer to instance of Car inside setInterval function.
To fix it you can use bind.
var run = setInterval(move.bind(tdi), 30);
or without additional reference
var run = setInterval(tdi.moveRight.bind(tdi), 30);
Also, due to behavior of this context in javascrip it is a good practice to cache this context inside constractor and methods defined using prototype. It can prevent from some issues.
For example:
Car.prototype.draw = function() {
var self_ = this;
//obtaining the image that the car will have
var carHtml = '<img src = "http://nostarch.com/images/car.png">';
// attaching the chosen image file to the current object being drawn
self_.carElement = $(carHtml);
//giving the image of the Car object its (x, y) position on the plane using css
self_.carElement.css({
position: "absolute",
left: self_.x,
top: self_.y
});
A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.
More information to understand this context in javascript here.

Behavior of "this" keyword in JavaScript is different from Object Oriented languages.
You can get around it by using .bind() like a another answer suggested. But you will keep bumping into issues around this area unless you understand how "this" behaves.
In short:
Typical OO: "this" refers to the instance of the object, method of which was called.
Javascript: "this" refers to how the function was called.
This is a good article to read:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
Understanding "this", "prototype" and "closure" are vital to writing good JavaScript code.

Related

Using the Revealing Module Pattern, why is this object changed?

Given the following code:
var House = function(x, y) {
var _posX;
var _posY;
function init(x,y) {
_posX = x;
_posY = y;
}
// Auto init
init(x, y);
// Public
return {
posX: _posX,
posY: _posY,
setPosition: function(x, y) {
_posX = x;
_posY = y;
}
};
};
If I create a new House object:
var house = new House(3,4);
And use the setPosition method to change the position:
house.setPosition(100,50);
I expected that the house position would still be 3,4.. But it however changed (which is actually what I want, but I don't understand how this is possible?) I dont'understand it since Javascript already returned the position which is 3,4 and I would expect it to be like that all the time, even if I change the position using the set method.
console.log(house.posX + ',' + house.posY); // 100,50 (why not 3,4?)
Bonus question: is there a proper way to do the init rather than placing it, ugly in the middle of the code?
This behaviour is due to a closure.
Closures are functions that refer to independent (free) variables
(variables that are used locally, but defined in an enclosing scope).
In other words, these functions 'remember' the environment in which
they were created.
_posx and _posy were defined in a surrounding scope and setPosition remembers it.
By the way, I think that init should be removed and you should directly assign _posx and _posy in your constructor function.

Creating a javascript class with an object containing functions

I am trying to create a javascript "class" and it is working somewhat good, but the Engine.tile.draw isn't working as intended. I cannot seem to get it to work inside Engine.start. Is it not possible to create an object and add a function inside it, like I did? How would you guys do it? Any help is appreciated. :)
var EngineClass = ( function () {
var Engine = function () {
this.canvas = document.getElementById('game');
this.handle = this.canvas.getContext('2d');
};
Engine.prototype.start = function (mapData) {
this.tile.draw(mapData);
};
Engine.prototype.tile = {
draw: function (x, y, tile) {
this.handle.fillText(tile, x * 16, y * 16);
};
}
return Engine;
})();
var Engine = new EngineClass();
The comments above saying you shouldn't try to force classes on JavaScript which is a prototypical language are correct.
Technically, the reason this doesn't work is that whenever you invoke a function using dot notation (e.g. something.method()), the function gets invoked with this bound to the left hand side of the dot. So in this case, when you say this.tile.draw(mapData), the tile.draw function gets invoked with this being the tile object, rather than the Engine object as you'd expect.
There are several ways to overcome this but the best advice is to shift your mindset to JavaScript's prototyping system instead of trying to force your class-based mindset on it.
Because this inside draw function will refer to the Engine.prototype.tile object, not what you expected.
Change
Engine.prototype.tile = {
draw: function (x, y, tile) {
this.handle.fillText(tile, x * 16, y * 16);
};
}
to
Engine.prototype.tile = function() {
var self = this;
return {
draw: function (x, y, tile) {
self.handle.fillText(tile, x * 16, y * 16);
};
};
}
And call it like:
Engine.prototype.start = function (mapData) {
this.tile().draw(mapData);
};
It doesn't really work to use sub -bjects like you are here:
Engine.prototype.tile = {
draw: function (x, y, tile) {
this.handle.fillText(tile, x * 16, y * 16);
};
}
The issue is that when you call Engine.tile.draw(), the this pointer inside the draw() method will be set to the tile object which is not what your code is assuming (your code assumes that this points to the Engine instance which is not what happens).
If you really want a sub-object like this, then you will need to intialize that sub-object in the Engine constructor so that each tile object is set up uniquely and then you will need to add its engine pointer to the tile instance data so that when Engine.tile.draw() is called, you can get the appropriate Engine instance from the this pointer that points to the tile object. But, this is all a mess and probably both unnecessary and the hard way of doing things.
You should probably either make tile its own object with its own instance data or put the draw method on the Engine object and just pass it some arguments that help it do its job.

Javascript Prototype: Replacement vs Addition [duplicate]

This question already has answers here:
this.constructor.prototype -- can't wholly overwrite, but can write individual props?
(2 answers)
Closed 8 years ago.
I'm working with a fairly simple Point2D data structure I built to be inheritable for say a Point3D in the future and I've followed all the guides and similar questions I can find, but none seem help with my issue.
I've defined Point2D as follows:
function Point2D (px, py)
{
this.x = px;
this.y = py;
Point2D.prototype =
{
constructor: Point2D,
move:function (mx, my)
{
this.x = mx;
this.y = my;
},
translate:function (dx, dy)
{
this.x += dx;
this.y += dy;
}
};
};
I instantiate the object as follows:
var p2d1 = new Point2D(2,3);
Then I call one of the methods as follows:
p2d1.move(1,2);
And the result is:
TypeError: Object #<Point2D> has no method 'move'
I have not idea why my methods don't resolve.
I've messed around with it for a good while and found that I can declare Point2D methods this way and they will work.
Point2D.prototype.move = function () {};
Can anyone explain why they first style of replacing the entire prototype does not work, but adding functions to the existing prototype does work?
When you call new Point() the first time, Point.prototype is still an "empty" prototype. I.e. the instance that is created doesn't inherit any methods.
You change (replace) the prototype after the instance was already created. JavaScript has assign by value, not assign by reference. Quick example:
var a = 5;
var b = {c: a};
a = 10;
b.c is still 5, since assigning to a doesn't change what b.c refers to.
Point2D.prototype.move = function () {};
works because you are not replacing Point2D.prototype, you are simply mutating the existing object.
Overall, assignments to *.prototype should take place outside the constructor:
function Point2D (px, py) {
this.x = px;
this.y = py;
};
Point2D.prototype = { };
I am not sure, but defining the prototype inside the declaration of the "class" is unusual and to me, hard to define exactly how things would be resolved. When doing manual inheritence, I tend to follow more these patterns:
function Foo() {
this.bar = ...
}
Foo.prototype.baz = function() { ... }
OR
function Foo() { ... }
function Bar() { ... }
Foo.prototype = new Bar();
OR
Foo.prototype = {
blah: ...
}
Also I wouldn't usually create a "constructor" property manually, as this is a side effect of setting the prototype, but I know some popular libraries do this. In the middle example above, Foo.prototype.constructor == Bar.
If you really want to warp your brain create a second instance of Point2D and watch it have the move method available and working!
So here is what is happening.
define Point2D class
create instance of Point2D class
create initialization object
create execution context object per new keyword usage
attach prototype to execution context (at this point just Object)
run constructor method
assign value of x
assign value of y
assign new prototype value to Point2D class
what you want to do is to move the prototype setting out to the same scope as the class definition.

In prototype inheritance program when a property not found in a specified object

All, After reading this post, and did some test based on it .
function Shape() {
this.x = 0;
this.y = 0;
};
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.log("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
//Shape.call(this); //call super constructor.
};
Rectangle.prototype = Object.create(Shape.prototype);
var rect = new Rectangle();
alert(rect.x);
If I commented the code Shape.call(this); in the Rectangle, I found the rect.x is underfined instead of the value 0.
And What make me confused is that I found in the best answer of the Post said:
"In javascript, every object has a secret link to the object which created it,forming a chain. When an object is asked for a property that it does not have,its parent object is asked... continually up the chain until the property is found or until the root object is reached."
So I can't understand why the rect can't found x in the prototype chain. The rect is already inherited form Shape. If the x doesn't exist in the rect, It supposed be found in his parent. right ?
And in my understanding .If using Shape.call(this); , It just add a new x property to the rect, well ,that would not be a code reusing the original x from parents. It just like the override property in the classical inheritance . that is add a new property into the sub class which have the same name and type as the one in the base class..I don't know if my understanding is right , if not . please correct me .Or was I missing something I didn't noticed ? thanks.
Edit
Below is my understanding based on the Thilo and Arun P Johny 's answers. please correct me if it is not right.
Before inheritance happen.
After inheritance executed.
So the x only belong to the instance constructed by Shape. thanks
If you don't call the super constructor, then this.x = 0 is not executed so x remains undefined.
If you wanted it to appear in the prototype, you'd have to say Shape.prototype.x = 0, I think.
So I can't understand why the rect can't find x in the prototype chain.
It's because the prototype does not have x either. It only has move. The x gets assigned to individual instances in the constructor (but only if you call it).
Arun P Johny is right (you should read his comment!)
Try this:
function Shape() {
this.x = 0;
this.y = 0;
};
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.log("Shape moved.");
};
// Rectangle - subclass
function Rectangle() {
};
Rectangle.prototype = new Shape();
var rect = new Rectangle();
alert(rect.x);
You can call Shape.call(this); like you did (commented) in your code, but this way it's not a "real" inheritance since you won't be able to use move() in Rectangle.
But the code above is a mishmash of "new" and prototypes and hence very confusing. I guess that what you really want to do is something like this:
var Shape = {
x: 0,
y: 0,
move: function(x, y) {
this.x += x;
this.y += y;
alert("Shape moved: ["+this.x+","+this.y+"]");
}
};
var rect = Object.create(Shape);
alert(rect.x);
rect.move(2,3);
rect.move(1,1);

Passing an object member (as this.member) to a class method in javascript

I'm trying to pass this.SOME_MEMBER to a method, but it doesn't go thru as that same member. The code below should make a little more sense at what I'm trying to get at.
function App()
{
this.canvas = new Object();
}
App.prototype.createCanvas = function(obj, width, height)
{
obj = document.createElement('canvas');
obj.setAttribute('width', width);
obj.setAttribute('height', height);
log(obj == this.canvas); //false
log(this.canvas == app.canvas); //true
//it's clear that this refers to app
//so why doesn't obj refers to this.canvas?
//how do i get obj to equal this.canvas?
}
App.prototype.init = function()
{
this.createCanvas(this.canvas, 800, 600);
log(this.canvas.width); //undefined
}
var app = new App();
app.init();
Now, I understand that i can simply just do the following:
function App()
{
this.canvas = new Object();
}
App.prototype.createCanvas = function(width, height)
{
this.canvas = document.createElement('canvas');
this.canvas.setAttribute('width', width);
this.canvas.setAttribute('height', height);
}
App.prototype.init = function()
{
this.createCanvas(800, 600);
log(this.canvas.width); //800
}
var app = new App();
app.init();
This would work for most applications but I'm restricted to naming the variable as this.canvas. I'll eventually need the method to be a little more flexible that that.
Keep in mind, that I'm using the functions document.createElement and element.setAttribute as a mere example; they could be replaced with anything that modifies the variable obj.
So when "this.canvas" is being used as an argument, what happens to it? How would i achieve what I'm looking to do?
Thanks in advance!
so why doesn't obj refers to this.canvas?
Because the first thing you've done in the function is give it a different value instead:
App.prototype.createCanvas = function(obj, width, height)
{
obj = document.createElement('canvas');
// ^------------------------------------------ here
obj.setAttribute('width', width);
obj.setAttribute('height', height);
log(obj == this.canvas); //false
log(this.canvas == app.canvas); //true
//it's clear that this refers to app
//so why doesn't obj refers to this.canvas?
//how do i get obj to equal this.canvas?
}
JavaScript is a purely pass-by-value language. The obj argument has the value you passed in (which comes from this.canvas because that's what you gave when calling it); obj is not a reference to the this.canvas property. To do what you wanted to do in the way you wanted to do it would require passing this.canvas (the property) by reference, which you can't do in JavaScript. (But see below.)
Now, I understand that i can simply just do the following:
(code omitted)
This would work for most applications but I'm restricted to naming the variable as this.canvas. I'll eventually need the method to be a little more flexible that that.
That's a somewhat strange requirement (surely all App instances should have the canvas in canvas), but if you want to be able to tell createCanvas what property on the object to put the canvas on, you can do that:
JavaScript supports two ways of referencing object properties: Dotted notation using literals (this.canvas), and bracketed notation using strings (this["canvas"]). In the latter case, the string doesn't have to be a literal, it can be the result of any expession — including a variable reference.
So your createCanvas becomes:
App.prototype.createCanvas = function(propName, width, height)
{
// Create the canvas
var obj = document.createElement('canvas');
obj.setAttribute('width', width);
obj.setAttribute('height', height);
// Save that canvas to the given property
this[propName] = obj;
}
and you call it like this:
App.prototype.init = function()
{
this.createCanvas("canvas", 800, 600);
}
That said, it would be rather more direct to just have createCanvas return the canvas so the calling code can put it wherever it wants:
App.prototype.createCanvas = function(width, height)
{
// Create the canvas
var obj = document.createElement('canvas');
obj.setAttribute('width', width);
obj.setAttribute('height', height);
return obj;
}
then
App.prototype.init = function()
{
this.canvas = this.createCanvas(800, 600);
}
Side note: You're missing semicolons, when you have x = ... it needs a ; at the end, even if the .. is a function expression. So for instance, the App.prototype.init = ... needs a ; at the end:
App.prototype.init = function()
{
this.canvas = this.createCanvas(800, 600);
}; // <=== Here
It doesn't cause a syntax error because of the horror that is automatic semicolon insertion, but I'd strongly recommend not relying on it, the edge cases where it does the wrong thing will bite you when you least expect it (and can least afford it). Always including the correct semicolons saves time in the long run.
Your line,
obj = document.createElement('canvas');
overrides the obj variable passed into the createCanvas method, and sets it as a new object entirely. The variable allocation breaks the previous reference, and creates a new reference to the new object. The obj variable is a reference to another object. You can't use it to change the referenced object into being a competely different object.
If you act on the obj variable by calling methods or changing properties, those will all act on the original object. So, maybe pass the this object into the method instead (although this seems a little pointless unless you're planning to call the function in a different scope).

Categories

Resources