Inherit objects. Iterate through all objects - javascript

Here is my problem:
I have an inherit objects(class) function, which I populate with x many objects, like so:
function Booking (doc_id, arrival_date, supplier_amount, client_amount, currency, profit, calculated_profit, currency_rate) {
this.doc_id = doc_id;
this.arrival_date = arrival_date;
this.supplier_amount = supplier_amount;
this.client_amount = client_amount;
this.currency = currency;
this.profit = profit;
this.calculated_profit = calculated_profit;
this.exchange_rate = currency_rate;
if(pastDate(this.arrival_date)) {
past_date: true;
}
else {
past_date: false;
}
}
Is it possible to iterate through all the objects?
I would like to have a function that iterates through all Booking objects, and uses the results to populate a dataTables table.
I guess the function would have to be defined by
Booking.prototype = { }
I canĀ“t seem to find anything about this on the web. I have tried all my ideas without success.

To iterate all Booking instances, you must store references to them somewhere:
var Booking = (function() {
var instances = []; // Array of instances
function Booking(foo) {
if (!(this instanceof Booking)) return; // Called without `new`
instances.push(this); // Store the instance
this.foo = foo; // Your current code
}
Booking.prototype.whatever = function() {
// You can use `instances` here
}
return Booking;
})();
But wait: DO NOT DO THAT (unless its strictly necessary).
The code above has a big problem: since the Booking instances are referenced in instances, the garbage collector won't kill them, even if they aren't referenced anywhere else.
Therefore, each time you create an instance, you produce a memory leak.
ECMAScript 6 introduces WeakSet, which lets you store weakly held objects in a collection, so that the garbage collector will kill them if they aren't referenced anywhere else. But WeakSets aren't iterable, so they aren't useful in your case.

Related

Javascript Flyweight with WeakMap or WeakSet

I want a Flyweight object so I created an Object and stored it's instances in a Map like this:
const FlyweightNumber = (function(){
"use strict";
const instances = new Map();
class FlyweightNumber{
constructor(number){
Object.defineProperty(this, 'number', {
value: number
});
if(!instances.get(number)){
instances.set(number, this);
}
else {
return instances.get(number);
}
}
toString() {
return this.number;
}
valueOf(){
return this.number;
}
toJSON(){
return this.number;
}
}
return FlyweightNumber;
})();
module.exports = FlyweightNumber;
The problem is that when I'm not using a FlyweightNumber value anymore it is still in memory, stored in this Map.
Since WeakMap and WeakSet are supposed to let garbage colector clear it if it is not used anymore, how could I write a constructor to return the object in the WeakSet or WeakMap or create a new object if it is not stored anymore?
You're looking for a soft reference to implement your number cache. Unfortunately, JS doesn't have these.
And its WeakMap doesn't create weak references either, it's an ephemeron actually. It does not allow you to observe whether an object has been collected.

Disable property mutation in JS

I was creating a component and was trying to break my implementation. The idea is to not allow user to manipulate the exposed properties.
The implementation was like this:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data; },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, setting the property is handled but user is still able to mutate it using .push. This is because I'm returning a reference.
I have handled this case like:
function MyClass(){
var data = [];
Object.defineProperty(this, 'data', {
get: function(){ return data.slice(); },
set: function(){ throw new Error('This operation is not allowed'); },
configurable: false,
});
}
var obj = new MyClass();
try {
obj.data = [];
} catch(ex) {
console.log('mutation handled');
}
obj.data.push('Found a way to mutate');
console.log(obj.data)
As you see, I'm returning a new array to solve this. Not sure how it will affect performance wise.
Question: Is there an alternate way to not allow user to mutate properties that are of type object?
I have tried using writable: false, but it gives me error when I use it with get.
Note: I want this array to mutable within class but not from outside.
Your problem here is that you are effectively blocking attempts to modify MyClass. However, other objects members of MyClass are still JavaScript objects. That way you're doing it (returning a new Array for every call to get) is one of the best ways, though of course, depending of how frequently you call get or the length of the array might have performance drawbacks.
Of course, if you could use ES6, you could extend the native Array to create a ReadOnlyArray class. You can actually do this in ES5, too, but you lose the ability to use square brackets to retrieve the value from a specific index in the array.
Another option, if you can avoid Internet Explorer, is to use Proxies (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy).
With a proxy, you can trap calls to get properties of an object, and decide what to return or to do.
In the example below, we create a Proxy for an array. As you see in the handler, we define a get function. This function will be called whenever the value of a property of the target object is accessed. This includes accessing indexes or methods, as calling a method is basically retrieving the value of a property (the function) and then calling it.
As you see, if the property is an integer number, we return that position in the array. If the property is 'length' then we return the length of the array. In any other case, we return a void function.
The advantage of this is that the proxyArray still behaves like an array. You can use square brackets to get to its indexes and use the property length. But if you try to do something like proxyArray.push(23) nothing happens.
Of course, in a final solution, you might want decide what to do based on which
method is being called. You might want methods like map, filter and so on to work.
And finally, the last advantage of this approach is that you keep a reference to the original array, so you can still modify it and its values are accessible through the proxy.
var handler = {
get: function(target, property, receiver) {
var regexp = /[\d]+/;
if (regexp.exec(property)) { // indexes:
return target[property];
}
if (property === 'length') {
return target.length;
}
if (typeof (target[property]) === 'function') {
// return a function that does nothing:
return function() {};
}
}
};
// this is the original array that we keep private
var array = [1, 2, 3];
// this is the 'visible' array:
var proxyArray = new Proxy(array, handler);
console.log(proxyArray[1]);
console.log(proxyArray.length);
console.log(proxyArray.push(32)); // does nothing
console.log(proxyArray[3]); // undefined
// but if we modify the old array:
array.push(23);
console.log(array);
// the proxy is modified
console.log(proxyArray[3]); // 32
Of course, the poblem is that proxyArray is not really an array, so, depending on how you plan to use it, this might be a problem.
What you want isn't really doable in JavaScript, as far as I'm aware. The best you can hope for is to hide the data from the user as best you can. The best way to do that would be with a WeakMap
let privateData = new WeakMap();
class MyClass {
constructor() {
privateData.set(this, {
data: []
});
}
addEntry(entry) {
privateData.get(this).data.push(entry);
}
getData() {
return privateData.get(this).data.concat();
}
}
So long as you never export privateData don't export from the module, or wrap within an IIFE etc.) then your MyClass instances will be able to access the data but external forces can't (other than through methods you create)
var myInstance = new MyClass();
myInstance.getData(); // -> []
myInstance.getData().push(1);
myInstance.getData(); // -> []
myInstance.addEntry(100);
myInstance.getData(); // -> [100]

Can a javascript function be a class and an instance of another object?

If you look at this code:
function supportAggregate(Meanio) {
Meanio.prototype.aggregated = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate === false){
return callback('');
}
if (aggregated[group][ext].data) return callback(aggregated[group][ext].data);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].data);
};
Meanio.prototype.aggregatedsrc = function(ext, group, callback) {
// Aggregated Data already exists and is ready
if (Meanio.Singleton.config.clean.aggregate !== false){
if(ext==='js'){
if(group==='header'){
return callback(['/modules/aggregated.js?group=header']);
}else{
return callback(['/modules/aggregated.js']);
}
}else if(ext==='css' && group==='header'){
return callback(['/modules/aggregated.css']);
}
return callback([]);
}
if (aggregated[group][ext].src) return callback(aggregated[group][ext].src);
// No aggregated data exists so we will build it
sortAggregateAssetsByWeight();
// Returning rebuild data. All from memory so no callback required
callback(aggregated[group][ext].src);
};
// Allows rebuilding aggregated data
Meanio.prototype.rebuildAggregated = function() {
sortAggregateAssetsByWeight();
};
Meanio.prototype.Module.prototype.aggregateAsset = function(type, asset, options) {
options = options || {};
if (!options.inline && !options.absolute && !options.url) {
asset = path.join(Meanio.modules[this.name].source, this.name, 'public/assets', type, asset);
}
Meanio.aggregate(type, asset, options, Meanio.Singleton.config.clean);
};
Meanio.onModulesFoundAggregate = function(ext, options) {
var config = Meanio.Singleton.config.clean;
var aggregator = new Aggregator(options, false, config);
for (var name in Meanio.modules) {
aggregator.readFiles(ext, path.join(process.cwd(), Meanio.modules[name].source, name.toLowerCase(), 'public'));
}
};
Meanio.aggregate = function(ext, asset, options, config) {
var aggregator;
options = options || {};
if (!asset) {
return;
}
aggregator = new Aggregator(options, true, config);
if (options.inline) return aggregator.addInlineCode(ext, asset);
else if (options.url) return aggregator.getRemoteCode(ext, asset);
else if (options.singlefile) return aggregator.processDirOfFile(ext, asset);
else return aggregator.readFile(ext, path.join(process.cwd(), asset));
};
Meanio.prototype.aggregate = Meanio.aggregate;
}
module.exports = supportAggregate;
(https://github.com/linnovate/meanio/blob/master/lib/aggregation.js#L213)
You can see that there are two types of functions for Meanio that are created. Also, by the way, you can see where this is instantiated here: https://github.com/linnovate/meanio/blob/master/lib/mean.js#L114
But I'm just confused. Sometime, Meanio functions are defined like this:
Meanio.prototype.myfunction = function() {}
and sometimes they are defined like this:
Meanio.myfunction = function() {}
I just don't get it; although I have a feeling that dependency injection is somehow involved.
How can this be? How can an object be both a class and an instance of itself?
This code is very confusing to me, and I would really appreciate it if someone could shed some light on this for me. I'm not asking you to heavily research the code, but if you could give me a general understanding, that would be great.
Thanks in advance!
How can an object be both a class and an instance of itself?
That's not what's going on here. The object passed to the function is an instance.
The function does however modify both the instance that you pass to it, and the class of that instance.
If you create two instances of the same class, and pass one of them to the function, the other instance is not modified, but the class that is common to them is modified. Example:
function MyClass() {}
var a = new MyClass();
var b = new MyClass();
supportAggregate(a);
Now both a.rebuildAggregated and b.rebuildAggregated exist, as that is added to the class. The a.onModulesFoundAggregate exists because it's added to the instance, but b.onModulesFoundAggregate doesn't exist.
(Note: The example won't actually work, as there is more going on. The class has to have some more properties to work with that function, the example is only to show the difference between properties added to the prototype and to the instance.)
Let's say I have a constructor
// First I will define a constructor
function MyClass() {
this.classproperty = 1;
}
In Javascript the constructor is also an object instance. When I use "this" keyword inside a constructor I'm telling that I want to create a new property inside a special object present in all javascript objects called prototype.
// Then I add a new property without using prototype obj
MyClass.newProperty = 2;
alert(MyClass.classproperty); // alert 1
alert(MyClass.newProperty); // alert 2
// It will work because I'm using the MyClass main Object
When you create a new instance from Myclass Obj. The new created object will inherit the prototype object from parent (the one used to instantiate), but not the properties added straight to MyClass obj:
var instance = new MyClass();
alert(instance.newProperty); // undefined because the new instance will
// inherit only what is inside prototype obj
I have to add it to the prototype object in order to new instances inherit the property;
Myclass.prototype.newProperty = 2;
var instance = new Myclass();
alert(instance.newProperty) // alert 2

Memoize a prototype method

I'm trying to use memoization to speed up my Javascript, but I need it on a proto method, and I need said method to have access to the this object, and it's giving me fits. Here's what I have:
MyObj.prototype.myMethod = function(){
var self = this
, doGetData = (function() {
var memo = []
, data = function(obj) {
var result = memo;
if (obj.isDirty) {
obj.isDirty = false;
result = $.map(obj.details, function(elem, i) {
return elem.export();
});
memo = result;
}
return result;
}
return data;
}())
;
return doGetData(self);
};
I can get it to run, I just can't get it to memoize. I know something is wrong, but I can't figure out what. I know there are tons of examples of how to memoize, but none that I've come across deal with scope like this.
If you want the function on the prototype, but you want it to memoize for each instance independently, then "memo" needs to be an instance property (or a closure could be the instance property, I guess; whichever).
MyObj.prototype.myMethod = function() {
if (!("memo" in this) || this.isDirty) {
this.isDirty =false;
this.memo = $.map(obj.details, function(elem, i) {
return elem.export();
});
}
return this.memo;
};
I don't think you can do it without polluting the instances, though you could use newer JavaScript features to keep the "memo" thing from being enumerable. The prototype is shared by all instances, after all.
It is possible to use a Map object for storing and retrieving method caches for all instances. Maps support objects as keys. (Kind of off-topic, but strict mode affects the propagation of "this". Now this works without "var self = this;".)
"use strict";
MyObj.prototype.myMethod = function() {
var prototypeCache = new Map();
return function(arg) {
if (!prototypeCache.has(this)) prototypeCache.set(this, new Map());
var instanceCache = prototypeCache.get(this);
if (instanceCache.has(arg)) return instanceCache.get(arg);
else {
//Do expensive calculation that is stored in result...
instanceCache.set(arg, result);
return result;
}
};
}();
Now for simplicity I also used a Map for the instanceCache. For speed one would typically use just an object. It depends on the keys one can make. It is typically possible to find a way to store by unique strings. (Numbers can be converted, Objects can have id strings and state/version properties, multiple arguments can be concatenated into one string...)
Another solution entirely is to create the method in the constructor instead of having it in the prototype. But that is just as bad pollution as having method caches in instance properties. Much worse, I think.

Object Oriented JavaScript programming

I have been trying to learn OOP with JavaScript before I start attempting to learn backbone.js.
I want to be able to data bind but I can't seem to get it to work.
I've just made a simple protoype of a budget website that you can put in a budget and input how much you've spent, and it will show if you've gone over.
function BudgetItem(spent, budget){
this.setSpent = function(spent){
this.spent = spent;
}
this.setBudget = function(budget){
this.budget = budget;
}
this.getSpent = function(){
return this.spent;
}
this.getBudget = function(){
return this.budget;
}
}
function BudgetType(type){
this.getType = function(){
return type;
}
}
BudgetType.prototype = new BudgetItem();
$(document).ready(function(){
var food = new BudgetType('food');
$('.budget').html(food.getBudget());
$('.editbudget').change(function(){
food.setBudget($('.editbudget').data())
});
})
That's my code thus far. I'm not sure if I'm doing it right. Am I supposed to extend things? Also, can someone explain how to dynamically data bind without a library?
First I'll give you some theory. A Javascript function is a dynamic object, just like Object is, and a new instance can be created using the new keyword much like you are doing in your listener. When this happens, the function itself will run as a constructor while the this keyword will be bound to the newly created object. What you're doing above then is in fact adding new properties on the fly as you're passing in their values for the first time... which is fine, but not very clear to another reader.
Now for the tricky part. Every function has a link to a "hidden" Prototype object. This is an anonymous (not accessible by name) object created by the JavaScript runtime and passed as a reference to the user object through the prototype property. This Prototype object also has a reference to the function through its constructor property. To test what I'm saying for yourself, try the following:
BudgetItem.prototype.constructor === BudgetItem // true
Putting it all together, you can now think of functions as constructors to (hidden) classes that are created for you behind the scenes, accessible through the function's prototype property. So, you could add the fields to the Prototype object directly as so:
function BudgetItem(spent) {
this.spent = spent
}
BudgetItem.prototype.setSpent = function(spent) { this.spent = spent };
BudgetItem.prototype.getSpent = function() { return this.spent };
Another problem is inheritance and passing parameters to the constructor. Again, your version is valid but you lose the ability to pass the spent and budget values when initializing a BudgetType. What I would do is forget prototypes and go:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.type = type;
return instance;
}
This is close to what Scott Sauyet suggested above but more powerful. Now you can pass both parameters (and more) and have a more complicated inheritance tree.
Finally, what you can do is create private (or pseudo-private, more accurately) properties by providing a getter to an otherwise automatic variable (one passed as an argument or initialised inside the function). This is a special feature of the language and it works like so:
function BudgetType(type, spent) {
var instance = new BudgetItem(spent);
instance.getType = function() {
return type;
}
return instance;
}
Now you can access the 'type' passed in the constructor by obj.getType() but cannot override the initial value. Even if you define obj.type = 'New Value' the getType() will return the initial parameter passed because it has a reference to another context which was created when the object was initialised and never got released due to the closure.
Hope that helps...
if you want all instances of objects to reference the same members/values you can use a closure:
// create a constrctor for you object wrapped in a closure
myCon = (function() {
// define shared members up here
var mySharedObj = new function () {
this.member = "a";
}();
// return the actual constructor
return function () {
this.mySharedObj = mySharedObj;
}
}());
// create two instances of the object
var a = new myCon();
var b = new myCon();
// Altering the shared object from one
a.mySharedObj.member = "b";
// Alters it for all
console.log(b.mySharedObj.member);
If you want to build objects from other objects(sort of like other languages' class whatever extends baseClass), but do not want them to share values via reference(instead a clone of values), you can use something like the following:
Object.prototype.extendsUpon = (function (_prop, _args) {
return function (base) {
for (var key in base) {
if (_prop.call(base, key)) {
this[key] = base[key];
}
}
function con(child){
this.constructor = child;
}
con.prototype = base.prototype;
this.prototype = new con(this);
this.__base__ = base.prototype;
var args = _args.call(arguments);
args.shift();
base.constructor.apply(this, args);
}
}(Object.prototype.hasOwnProperty, Array.prototype.slice));
Then to build objects ontop of objects:
// Base Object Constructor
function Fruit(name) {
this.fruitname = name;
}
Fruit.prototype.yum = function() {
return "I had an " + this.fruitname;
}
// Object constructor that derives from the Base Object
function Favorite() {
// Derive this object from a specified base object:
// #arg0 -> Object Constructor to use as base
// #arg1+ -> arguments passed to the BaseObject's constructor
this.extendsUpon(Fruit, "apple");
// From here proceed as usual
// To access members from the base object that have been over-written,
// use "this.__base__.MEMBER.apply(this, arguments)"
}
Favorite.prototype.yum = function() {
return this.__base__.yum.apply(this) + " and it was my favorite";
}
var mmm = new Favorite();
// Outputs: "I had an apple and it was my favorite"
mmm.yum();

Categories

Resources