For example, take a look at my simple implementation of a stack:
var MyStack = (function() {
var min;
var head;
// Constructor
function MyStack() {
this.size = 0;
}
MyStack.prototype.push = function(val) {
var node = new Node(val);
if (typeof min === 'undefined' || val < min) {
min = val;
}
++this.size;
if (typeof head === 'undefined') {
head = node;
} else {
node.next = head;
head = node;
}
};
MyStack.prototype.pop = function() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
--this.size;
var data = head.data;
head = head.next;
return data;
};
MyStack.prototype.min = function() {
if (typeof min === 'undefined') {
throw new Error('Min not defined');
}
return min;
};
MyStack.prototype.peek = function() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
return head.data;
};
function Node(data) {
this.data = data;
this.next;
}
return MyStack;
})();
By using this method, I can make sure that no one is able to (accidentally or intentionally) manipulate "private" fields such as min and head. I can also make use of private functions such as Node() which doesn't need to be exposed.
I have read that this will use more memory just because of the fact that it has to maintain an additional scope for each new object that is created for MyStack. Does it need so much extra memory that this way is a bad idea?
I did try to optimize it by making use of prototypes instead of creating functions every time a new object is created. In other words, I didn't include the functions as part of the constructor of MyStack.
My question is, is this poor design? Are there are any major downfalls to this methodology?
Is using closures to emulate encapsulation a bad idea?
No. Although I wouldn't consider this to be "emulation", closures are implementing encapsulation.
I did try to optimize it by making use of prototypes instead of creating functions every time a new object is created. In other words, I didn't include the functions as part of the constructor of MyStack.
My question is, is this poor design? Are there are any major downfalls to this methodology?
Yes, that's actually wrong. Your min
and head
(and MyStack
and Node
) variables are essentially static. They are defined only once, and will be shared by all instances. You cannot create two distinct stacks, they both will have the same head
reference.
To encapulate per-instance state, you will need to declare the variables in the constructor so that they will be created with every new object. For that, you'll have to also declare all methods that need to access them ("privileged") in the constructor scope.
var MyStack = (function() {
function MyStack() {
var size = 0;
var head = undefined;
function checkNonEmpty() {
if (typeof head === 'undefined') {
throw new Error('Empty stack');
}
}
this.push = function(val) {
size++;
head = new Node(val, head);
};
this.pop = function() {
checkNonEmpty();
this.size--;
var data = head.data;
head = head.next;
return data;
};
this.peek = function() {
checkNonEmpty();
return head.data;
};
this.getSize = function() {
return size;
};
}
function Node(data, next) {
this.data = data;
this.next = next;
}
return MyStack;
})();
If you want to put these methods on the prototype, you will need to make the head
and size
values available as properties of the instance.
Considering that encapsulation is one of the principles of of Object Oriented Programming, I don't think this is a bad coding practice. Closures in JavaScript are your means of encapsulating variables and restricting access to other parts of the application.
In a lot of ways, nothing is truly secure in JavaScript. Even with a closure, those variables you want keep private are accessible via different means in different browsers. For example, in Google Chrome, you can set a break-point in the debugger and get access to any variable within the active enclosure. Although we can do a great deal within the browser to ensure security, we are still dealing with an interpreted language that is compiled on the machine running the code.
And considering your example:
var MyStack = (function(){...})();
If you ever work with typescript or any rapid development frameworks, you will see that this is the output coding method used to namespace objects when the frameworks compile/transpile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With