encapsulation in javascript module pattern - javascript

I was reading this link http://addyosmani.com/largescalejavascript/#modpattern
And saw the following example.
var basketModule = (function() {
var basket = []; //private
return { //exposed to public
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
},
getTotal: function(){
var q = this.getItemCount(),p=0;
while(q--){
p+= basket[q].price;
}
return p;
}
}
}());
basketModule.addItem({item:'bread',price:0.5});
basketModule.addItem({item:'butter',price:0.3});
console.log(basketModule.getItemCount());
console.log(basketModule.getTotal());
It stats that "The module pattern is a popular design that pattern that encapsulates 'privacy', state and organization using closures" How is this different from writing it like the below? Can't privacy be simply enforced with function scope?
var basketModule = function() {
var basket = []; //private
this.addItem = function(values) {
basket.push(values);
}
this.getItemCount = function() {
return basket.length;
}
this.getTotal = function(){
var q = this.getItemCount(),p=0;
while(q--){
p+= basket[q].price;
}
return p;
}
}
var basket = new basketModule();
basket.addItem({item:'bread',price:0.5});
basket.addItem({item:'butter',price:0.3});

In the first variant you create an object without the possibility to create new instances of it (it is an immediately instantiated function). The second example is a full contructor function, allowing for several instances. The encapsulation is the same in both examples, the basket Array is 'private' in both.
Just for fun: best of both worlds could be:
var basketModule = (function() {
function Basket(){
var basket = []; //private
this.addItem = function(values) {
basket.push(values);
}
this.getItemCount = function() {
return basket.length;
}
this.getTotal = function(){
var q = this.getItemCount(),p=0;
while(q--){
p+= basket[q].price;
}
return p;
}
}
return {
basket: function(){return new Basket;}
}
}());
//usage
var basket1 = basketModule.basket(),
basket2 = basketModule.basket(),

Related

Variables Pointing To The Same Function

I have created a new variable, carBasket and foodBasket, and set them equal to the basketModule() function. They however are pointed to the same function when I want each of these two variables pointed to their own function. I am wondering what should I be doing to achieve this?
var basketModule = (function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
}());
carBasket = basketModule;
carBasket.addItem('Audi');
foodBasket = basketModule;
foodBasket.addItem('Ham');
foodBasket.getItemCount(); //outputs 2 instead of 1
You must call a function for each object in order to generate different variables for each one, e.g:
var basketModule = function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
};
var carBasket = basketModule(),
foodBasket = basketModule();
carBasket.addItem('Audi');
foodBasket.addItem('Ham');
foodBasket.getItemCount(); // 1
However, in order to reuse the methods for all instances, better use a constructor:
var BasketModule = function() {
this.basket = [];
};
BasketModule.prototype.addItem = function(values) {
this.basket.push(values);
};
BasketModule.prototype.getItemCount = function() {
return this.basket.length;
};
var carBasket = new BasketModule(),
foodBasket = new BasketModule();
carBasket.addItem('Audi');
foodBasket.addItem('Ham');
foodBasket.getItemCount(); // 1
You should consider trying this pattern instead:
var BasketModule = function() {
var basket = [];
return {
addItem: function(values) {
basket.push(values);
},
getItemCount: function() {
return basket.length;
}
};
};
carBasket = new BasketModule();
carBasket.addItem('Audi');
foodBasket = new BasketModule();
foodBasket.addItem('Ham');
jsfiddle: https://jsfiddle.net/nvsbjset/
This will create separate objects for each basket

A Javascript function which creates an object which calls the function itself

I am trying to make an angular service that returns a new object.
That's fine and good and works. new MakeRoll() creates an instance. But self.add, near the end also calls new MakeRoll() and that doesn't create an instance when I call add like I think it should.
I'm probably doing this all wrong but I haven't been able to figure it out.
var services = angular.module('services', []);
services.factory('Roll', [function() {
var MakeRoll = function () {
var self = {};
self.rolls = [];
self.add = function(number, sizeOfDice, add) {
var newRoll = {};
newRoll.number = number || 1;
newRoll.sizeOfDice = sizeOfDice || 6;
newRoll.add = add || 0;
newRoll.rollDice = function() {
var result = 0;
var results=[];
for (var i = 0; i < newRoll.number; i++) {
var roll = Math.floor(Math.random() * newRoll.sizeOfDice) + 1;
result += roll;
results.push(roll);
}
newRoll.results = results;
newRoll.result = result;
newRoll.Roll = new MakeRoll();
};
self.rolls.push(newRoll);
return self;
};
self.remove = function(index) {
self.rolls.splice(index, 1);
};
self.get = function(index) {
return self.rolls[index];
};
return self;
};
return new MakeRoll();
}
]);
angular service is designed to be singleton to accomplish some business logic, so don't mix up plain model with angular service. if you want to have more objects, just create a constructor and link it in service to be operated on.
function MakeRoll() {
...
}
angular.module('service', []).factory('Roll', function () {
var rolls = [];
return {
add: add,
remove: remove,
get: get
}
function add() {
// var o = new MakrRoll();
// rolls.push(o);
}
function remove(o) {
// remove o from rolls
}
function get(o) {
// get o from rolls
}
});

Why is my effort to implement Parasitic Inheritance in Javascript failing here?

I am playing with classical inheritance in Javascript (in Chrome) following the example of Crockford. I like the parasitic interface but am looking for a bit cleaner way to encapsulate inheritance.
There are a couple additional requirements I am trying to meet:
I'd like to be able to override methods of the parent class with methods in the child class that can call the parent method
I don't want to have to re-declare properties of the parent class in the child class.
This is my parent class Parasite with an extend method that tries to encapsulate inheritance and take advantage of Object.defineProperty:
function Parasite(host) {
var self = {};
self.host = host;
self.swollen = false;
self.init = function() {
console.debug('Parasite.init');
return self;
};
self.suck = function() {
console.log("I'm a parasite who sucks on " + self.host);
self.swollen = true;
return self;
};
self.extend = function(child) {
for(var prop in self) {
if (prop == 'extend') { // skip extend
console.debug('skip extend');
continue;
}
var is_extended = child.hasOwnProperty(prop);
var is_accessor = typeof self[prop] != "function";
// inherit prop
if (! is_extended) {
child[prop] = self[prop];
console.debug('prop', prop, 'inherited by child');
}
// default: override
else {
console.debug('prop', prop, 'overridden by child');
}
// For accessors, parent should reference child. This tries to
// synchronize them on the child's accesor.
if (is_accessor) {
var accessor = prop.toString();
console.warn('define accessor for', accessor, ':', child[accessor]);
Object.defineProperty(self, accessor, {
get: function() {
var val = child[accessor];
console.debug('getting', accessor, val, 'from', child, 'for', self);
return val;
},
set: function(val) {
console.debug('setting', accessor, val, 'from', child, 'for', self);
child[accessor] = val;
},
enumerable: true,
configurable: true
});
};
}
child.parent = self;
return child;
};
self = self.init();
return self;
}
This is my child class, Tick:
function Tick(host) {
var self = {};
self.suck = function() {
self.parent.suck.call();
self.engorge();
};
self.engorge = function() {
console.log("And now I'm engorged with blood.");
};
self.init = function() {
var parent = new Parasite(host);
self = parent.extend(self);
return self;
};
self = self.init();
return self;
}
You can find my latest fiddle here (warning: infinitely recurses in Firefox, but not in Chrome):
http://jsfiddle.net/SSDgv/23/
The console output illustrates where the accessor issues are occurring.
This isn't an answer to my question, per se, but here's a much simpler implementation that seems to meet my requirements. It follows the pattern presented in this Stack Overflow answer.
function Parasite(host) {
var self = {};
self.host = host;
self.hungry = true;
self.init = function() {
console.debug('Parasite.init');
return self;
};
self.suck = function() {
console.log("I'm a parasite who sucks on " + self.host);
self.hungry = false;
return self;
};
self.init();
return self;
}
function Tick(host) {
var self = new Parasite(host);
self.engorged = false;
var base_suck = self.suck;
self.suck = function() {
base_suck();
self.engorge();
};
self.engorge = function() {
console.log("And now I'm engorged with blood.");
self.engorged = true;
};
self.init = function() {};
self.init();
return self;
}
Fiddle with tests can be found here: http://jsfiddle.net/AvdK2/3/

JavaScript Closures Manipulation

I'm doing some Node.js and I want to use the closure representation to create my objects. I think I'm missing something, because something simple like this isn't working:
var Room = function(foo) {
this.name = foo;
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room("foo");
console.log(room.getName());
I also have tried without the parameter.. and still not working.
var Room = function() {
this.name = "foo";
this.users= [];
return {
getName : function() {
return this.name;
}
}
}
var room = new Room();
console.log(room.getName());
However, something like this works:
var Room = function(foo) {
this.name = foo;
this.users= [];
}
var room = new Room("foo");
console.log(room.name);
I can't understand why this isn't working.
--Edited
Thanks to Amadan I have found the right way to do it:
var Room = function() {
var name = "foo";
var users= [];
return {
getName : function() {
return name;
}
}
}
var room = new Room();
console.log(room.getName());
This way "name" and "users" are encapsulated.
return in a constructor will overwrite this. So the right way to do this is:
var Room = function(foo) {
this.name = foo;
this.users= [];
this.getName = function() {
return this.name;
}
}
or
var Room = function(foo) {
return {
name: "foo",
users: [],
getName : function() {
return this.name;
}
}
}
The first one does everything on the original this; the second one replaces this with everything you need.

implementing extend method in javascript

By looking through the code of BackboneJS, i am interested about extend the implemented. by when i try to make it myself i am stuck. my code is the following.
var extend = function(child) {
var base = this;
if(child) {
for(var prop in child) {
base[prop] = child[prop];
}
}
return base;
};
var Test = Mod.Test = function() {
this.data = {};
}
Test.prototype.set = function(key, value) {
this.data[key] = value;
}
Test.prototype.get = function(key) {
return this.data[key];
}
Test.extend = extend;
when i try like this i am not able to attach hello method to Mod.Test
var testObj = new Mod.Test.extend({
hello : function() {
console.log('hello');
}
});
How is it possible. how its implemented in backbonejs.
Backbone's extend method accepts two parameters - instance properties and static properties. The first ones are copied to the instance being created, the second are assigned to the instance's prototype. Usually you should invoke the extend method without the new operator but in this case here is a working version of your code:
var extend = function(child) {
var base = this;
if(child) {
for(var prop in child) {
base[prop] = child[prop];
}
for(var prop in child) {
base.prototype[prop] = child[prop];
}
}
return base;
};
var Test = Backbone.Model.Test = function() {
this.data = {};
}
Test.prototype.set = function(key, value) {
this.data[key] = value;
}
Test.prototype.get = function(key) {
return this.data[key];
}
Test.extend = extend;
and then:
Test = Backbone.Model.Test.extend({
hello : function() {
console.log('hello');
}
});
var testObj = new Test;

Categories

Resources