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.
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'm maintaining some legacy code and I've noticed that the following pattern for defining objects is used:
var MyObject = {};
(function (root) {
root.myFunction = function (foo) {
//do something
};
})(MyObject);
Is there any purpose to this? Is it equivalent to just doing the following?
var MyObject = {
myFunction : function (foo) {
//do something
};
};
I'm not about to embark in a holy quest to refactor the whole codebase to my likings, but I'd really like to understand the reason behind that roundabout way of defining objects.
Thanks!
It's called the module pattern http://toddmotto.com/mastering-the-module-pattern/
The main reason is for you to create truly private methods and variables. In your case, it's not meaningful because it's not hiding any implementation details.
Here's an example where it makes sense to use the module pattern.
var MyNameSpace = {};
(function(ns){
// The value variable is hidden from the outside world
var value = 0;
// So is this function
function adder(num) {
return num + 1;
}
ns.getNext = function () {
return value = adder(value);
}
})(MyNameSpace);
var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3
Whereas if you just used a straight object, adder and value would become public
var MyNameSpace = {
value: 0,
adder: function(num) {
return num + 1;
},
getNext: function() {
return this.value = this.adder(this.value);
}
}
And you could break it by doing stuff like
MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function
But with the module version
MyNameSpace.getNext(); // 1
// Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3
The purpose is to limit accessibility of functions within the closure to help prevent other scripts from executing code on it. By wrapping it around a closure you are redefining the scope of execution for all code inside the closure and effectively creating a private scope. See this article for more info:
http://lupomontero.com/using-javascript-closures-to-create-private-scopes/
From the article:
One of the best known problems in JavaScript is its dependance on a
global scope, which basically means that any variables you declare
outside of a function live in the same name space: the ominous
window object. Because of the nature of web pages, many scripts from
different sources can (and will) run on the same page sharing a
common global scope and this can be a really really bad thing as it
can lead to name collisions (variables with the same names being
overwritten) and security issues. To minimise the problem we can use
JavaScript’s powerful closures to create private scopes where we can
be sure our variables are invisible to other scripts on the page.
Code:
var MyObject = {};
(function (root) {
function myPrivateFunction() {
return "I can only be called from within the closure";
}
root.myFunction = function (foo) {
//do something
};
myPrivateFunction(); // returns "I can only be called from within the closure"
})(MyObject);
myPrivateFunction(); // throws error - undefined is not a function
advantages:
maintains variables in private scope.
you can extend the functionality of the existing object.
performance is increased.
i think the above three simple points are just enough to follow those rules. And to keep it simple its nothing but writing inner functions.
In the particular case that you show, there is no meaningful difference, in terms of functionality or visibility.
It's likely that the original coder adopted this approach as a sort of template allowing him to define private variables that could be used in the definition of things like myFunction:
var MyObject = {};
(function(root) {
var seconds_per_day = 24 * 60 * 60; // <-- private variable
root.myFunction = function(foo) {
return seconds_per_day;
};
})(MyObject);
This avoids calculating seconds_per_day each time the function is called, while also keeping it from polluting the global scope.
However, there's nothing essentially different from that and just saying
var MyObject = function() {
var seconds_per_day = 24 * 60 * 60;
return {
myFunction: function(foo) {
return seconds_per_day;
}
};
}();
The original coder may have preferred to be able to add functions to the object using the declarative syntax of root.myFunction = function, rather than the object/property syntax of myFunction: function. But that difference is mainly a matter of preference.
However, the structure taken by the original coder has the advantage that properties/methods can be easily added elsewhere in the code:
var MyObject = {};
(function(root) {
var seconds_per_day = 24 * 60 * 60;
root.myFunction = function(foo) {
return seconds_per_day;
};
})(MyObject);
(function(root) {
var another_private_variable = Math.pi;
root.myFunction2 = function(bar) { };
})(MyObject);
Bottom line, there is no need to adopt this approach if you don't need to, but there is also no need to change it, since it works perfectly well and actually has some advantages.
First pattern can be used as a module which takes an object and returns that object with some modifications. In other words, you can define such modules as follows.
var module = function (root) {
root.myFunction = function (foo) {
//do something
};
}
And use it like:
var obj = {};
module(obj);
So an advantage could be the re-usability of this module for later uses.
In the first pattern, you can define a private scope to store your private stuff such as private properties and methods. For example, consider this snippet:
(function (root) {
// A private property
var factor = 3;
root.multiply = function (foo) {
return foo * factor;
};
})(MyObject);
This pattern can be used to add a method or property to all types of objects such as arrays, object literals, functions.
function sum(a, b) {
return a + b;
}
(function (root) {
// A private property
var factor = 3;
root.multiply = function (foo) {
return foo * factor;
};
})(sum);
console.log(sum(1, 2)); // 3
console.log(sum.multiply(4)); // 12
In my opinion the main advantage could be the second one (creating a private scope)
This pattern provides a scope in which you can define helper functions that are not visible in the global scope:
(function (root) {
function doFoo() { ... };
root.myFunction = function (foo) {
//do something
doFoo();
//do something else
};
})(MyObject);
doFoo is local to the anonymous function, it can't be referenced from outside.
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.
Help,
I have this class
var jMath = {
pi2: Math.PI,
foo: function() {
return this.pi2;
}
}
I want to make the pi2 constant and i want jMath to inherit from Math object. How do I do that?
Oh amusing, scratch all that, this is the correct version:
function JMath() {
this.foo = function() {
return this.PI;
}
}
JMath.prototype = Math;
var jMath = new JMath();
alert(jMath.foo());
(which matches what the other answer is here)
(I originally tried to set the prototype using "JMath.prototype = new Math()" which is how I've seen it other places, but the above works)
Edit
Here's one way to do it as a singleton
// Execute an inline anon function to keep
// symbols out of global scope
var jMath = (function()
{
// Define the JMath "class"
function JMath() {
this.foo = function() {
return this.PI;
}
}
JMath.prototype = Math;
// return singleton
return new JMath();
})();
// test it
alert( jMath.PI );
// prove that JMath no longer exists
alert( JMath );
Consider using prototype:
function JMath() {};
JMath.prototype = {
pi2: Math.PI,
foo: function() {
return this.pi2;
}
}
var j = new JMath();
j.pi2=44; j.foo(); // returns 44
delete j.pi2; j.foo(); // now returns Math.PI
The difference between this and #altCognito's answer is that here the fields of the object are shared and all point to the same things. If you don't use prototypes, you create new and unlinked instances in the constructor. You can override the prototype's value on a per-instance basis, and if you override it and then decide you don't like the override value and want to restore the original, use delete to remove the override which merely "shadows" the prototype's value.
Edit: if you want to inherit all the methods and fields of the Math object itself, but override some things without affecting the Math object, do something like this (change the name "Constructor1" to your liking):
function Constructor1() {};
Constructor1.prototype = Math;
function JMath() {};
JMath.prototype = new Constructor1();
JMath.prototype.pi2 = JMath.prototype.PI;
JMath.prototype.foo = function() { return this.pi2; }
var j = new JMath();
j.cos(j.foo()); // returns -1
edit 3: explanation for the Constructor1 function: This creates the following prototype chain:
j -> JMath.prototype -> Math
j is an instance of JMath. JMath's prototype is an instance of Constructor1. Constructor1's prototype is Math. JMath.prototype is where the overridden stuff "lives". If you're only implementing a few instances of JMath, you could make the overridden stuff be instance variables that are setup by the constructor JMath, and point directly to Math, like #altCognito's answer does. (j is an instance of JMath and JMath's prototype is Math)
There are 2 downsides of augmenting-an-object-in-the-constructor. (Not actually downsides necessarily) One is that declaring instance fields/methods in the constructor creates separate values for each instance. If you create a lot of instances of JMath, each instance's JMath.foo function will be a separate object taking up additional memory. If the JMath.foo function comes from its prototype, then all the instances share one object.
In addition, you can change JMath.prototype.foo after the fact and the instances will update accordingly. If you make the foo function in the constructor as a per-instance method, then once JMath objects are created, they are independent and the only way to change the foo function is by changing each one.
edit 2: as far as read-only properties go, you can't really implement them from within Javascript itself, you need to muck around under the surface. However you can declare so-called "getters" which effectively act as constants:
JMath.prototype.__defineGetter__("pi2", function() { return Math.PI; });
JMath.prototype.__defineSetter__("pi2", function(){}); // NOP
var j = new JMath();
j.pi2 = 77; // gee, that's nice
// (if the setter is not defined, you'll get an exception)
j.pi2; // evaluates as Math.PI by calling the getter function
Warning: The syntax for defining getters/setters apparently is not something that IE doesn't implement nicely.
User-defined object properties can't be constant. Math (and a few other objects) is a special built-in - it has read-only properties and functions. But it's not a constructor - it's just a static object (Math.constructor === Object).
And because JavaScript has prototypal inheritance, and not classical, you can't inherit from Math. (Read more here)
What you can do, however, is define a prototype. When a property isn't found locally, the JS parser looks for that property on the current object's prototype. altCognito's current solutions shows this very well.
I'm curious about exactly what it is you're trying to achieve. Perhaps something like this is what you want?
var jMath = function()
{
const pi2 = Math.PI;
this.getPi2 = function()
{
return pi2;
}
}
var j = new jMath;
alert( j.getPi2() );