I need a super-readable version of this super simple inheritance in JavaScript. This is some auto-generated code, where I can't afford to be using external functions or libraries.
What I really want is, assuming Point3d "inherits" from Point, I want something like this:
function Point(x,y) {
this.x = x;
this.y = y;
}
function Point3d(x,y,z) {
Point(x, y);
this.z = z;
}
Except it doesn't actually work:
var pt = new Point3d(230,30,11);
// but x and y are in global scope, they should be in pt.x and pt.y :(
One possible option, would in the code generation duplicate all the members -- but since Javascript is prototype based, I would imagine this is easy to do properly (if I actually knew Javascript)
Thanks
Apply the Point constructor to the Point3d object using .call().
function Point3d(x,y,z) {
Point.call(this, x, y);
this.z = z;
}
http://jsfiddle.net/MSfu5/
The .call method sets the value of this in the function you're calling.
Here we're setting it to the new Point 3d object being constructed.
If there's anything on the Point prototype that Point3d should inherit, you can make the Point3d.prototype object an empty instance of Point.
You need to create an object to begin with.
function Point(x,y) {
return { x: x, y: y }
}
function Point3d(x,y,z) {
var self=new Point(x, y);
self.z=z;
return self;
}
var pt = new Point3d(230,30,11);
Related
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.
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.
Seems I didn't understand the constructor concept, So, I wrote some code to test it. Say you have the code like this:
var test=function(){...}
I know there is a property named constructor in the test.prototype object which point to the test object.
Here comes my question:
Is this property(constructor) only belongs to the prototype object ? or Do all the objects have the constructor property?
And I did another test. the code like below:
function Shape() {
this.x = 0;
this.y = 0;
}
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info("Shape moved.");
};
Rectangle = Object.create(Shape);//inherit from the Shape instead of Shape.prototype
Rectangle.constructor==Function//it is true.
I don't know where does Rectangle.constuctor come from or does it inherit from the Shape? thanks.
Object.create returns an object whose prototype is the object you pass it.
Therefore, since Shape.constructor is Function (Shape is a Function object), Rectangle inherits that.
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);
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);