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.
Related
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.
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.
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.
I have a Class that has a private variable with a public setter/getter function:
function Circle(rad) {
var r = rad;
this.radius = function(rad) {
if(!arguments.length) return r;
r = rad;
return this;
}
}
var shape = new Circle(10);
console.log( shape.radius() ); // 10
shape.r = 50;
console.log( shape.radius() ); // 10
How can I replicate this using Object.prototype? Or, when would I want to use a closure instead of Object.prototype? This is the closest I could come up with, but as you can see, you can change the property directly.
function Circle(r) {
this.r = r;
}
Circle.prototype.radius = function(r) {
if(!arguments.length) return this.r;
this.r = r;
return this;
};
var shape = new Circle(10);
console.log( shape.radius() ); // 10
shape.r = 50;
console.log( shape.radius() ); // 50
If you're going to use the prototype to store an object's properties, they are accessible from any code that has a reference to the object. It's impossible to do what you want.
What many JS devs do is just name private properties with a leading underscore so that others know not to mess with it, but it doesn't give you any real protection beyond a suggestion
Reasons to use closure based approach
True private variables, be confident that no one will mess with your privates
Reasons to use prototype
Less memory used (no closures for every instance)
Easier to debug (properties are visible on the object itself)
Allows monkey patching
Readers: Please edit the answer with reasons for whatever you think is the best solution.
I find your first Circle very odd. If you re-write it like this:
function Circle(rad) {
var r = rad;
this.radius = function(rad) {
if(rad){r = rad;}
return r;
}
}
I think it now does what you mean. r is private and radius acts as a getter/setter function.
Setting r like this:
shape.r = 50;
doesn't make sense because your first Circle doesn't have a property r, it only has a locally scoped variable r. It should raise some kind of error.
I can't see a way of using prototype in your second version of Circle because the function in the prototype chain wouldn't have access to a variable created in the Circle object. And anyway, in your second version, r is a property of Circle not a privately scoped variable in the function body.
I am wondering whether it is possible to inherit constructor in javascript. In the following example, I'd like the Moveable to assign x and y arguments to this.x and this.y respectivelly, as I defined in Sprite. Also, what would be the best way (but still short and readable) to define the prototype without creating the instation of ancestor? It would be best to assign it in the class itself, not in the outside scope as I it is now:
function Sprite(x, y) {
this.x = x ? x : 0;
this.y = y ? y : 0;
this.getPos = function() {
return {
x: this.x,
y: this.y
};
};
}
function Moveable(x, y) {
}
Moveable.prototype = new Sprite();
The standard way to call a superclass constructor is using Function.call:
function Moveable(x, y) {
Sprite.call(this, x, y);
}
As for the prototype, you can do something like this to chain the prototype without creating an instance of the superclass:
function makePrototype(superclass) {
function f() { }
f.prototype = superclass.prototype;
return new f();
}
Moveable.prototype = makePrototype(Sprite);
This uses a dummy constructor to create an object that shares the same prototype as Sprite, and since that's all JavaScript cares about, instances of Moveable are considered instanceof Sprite.
This isn't "short and readable" as you asked for, but the only other choice is to entirely skip prototypes and assign members directly within the constructor.
Edit: As #Raynos points out, you also want to set the constructor property (which is done by default by JavaScript but is lost as soon as you reset Moveable.prototype):
Moveable.prototype.constructor = Moveable;
You'd call the parent constructor like this:
function Moveable(x, y) {
Sprite.call(this, x, y);
}
I'm afraid there's no short way of setting up the inheritance if you want to use pseudo-classical inheritance, and no way of doing it inside the scope of the constructor function.
You can get around instantiating your base class though, if you construct a temporary empty object. Looks complicated but is commonly used as a helper function (like in the Google Closure Library goog.inherits method from where I more or less copied this):
var inherits = function(childConstructor, parentConstructor) {
function tempConstructor() {};
tempConstructor.prototype = parentConstructor.prototype;
childConstructor.prototype = new tempConstructor();
childConstructor.prototype.constructor = childConstructor;
};
inherits(Moveable, Sprite);
// instantiating Moveable will call the parent constructor
var m = new Moveable(1,1);
Think of a function as two pieces: the constructor function and the prototype object. Take two of these function classes and mix them together. Mixing the objects are simple enough, the trick is to mix the constructors.
var Sprite = function(x, y, w, h){
console.log("Sprite constr:", x, y, w, h);
}
var Moveable = function(x, y, w, h){
console.log("Moveable constr:", x, y, w, h);
}
var extend = function(class1, class2){
// here we make a new function that calls the two constructors.
// This is the "function mix"
var f = function(){
class1.prototype.constructor.apply(this, arguments);
class2.prototype.constructor.apply(this, arguments);
}
// now mix the prototypes
f.prototype = library.objectmix(class1.prototype, class2.prototype);
return f;
}
var MoveableSprite = extend(Sprite, Moveable);