I am working on a pretty ugly library that allows you to do some weird stuff. With a graph, you can map a collection of collections in a chain-like style, and when you change a value that needs to be changed throughout the system.
The problem occurred when the final type is a JS primitive.
In my case, after creating a graph with values ββand objects, I can do something like this:
CHAIN.components[0].value = 20;
components is a filter function over graph nodes using setters and getters. If there is only one node in the components, then the default value set by the user will be available without this: CHAIN.components.value = 20; But rather it is: CHAIN.components = 20;
Now the problem is that node can have other methods or properties besides the default value (which in my case is set to value .
How can I use setters and getters on the Number object without hacking into Number.prototype, because CHAIN.components now a number (if it's not a primitive, I made it work in an unobtrusive way), but when I want to call CHAIN.components.func() , the problem arises because I will have to add func to Number.prototype every time I create a set or get components , and then delete it.
Do you have another idea for doing this?
You need a code, here it is:
GRID.modules.OHM || Object.extend(GRID.modules, ( function() { var Node, Nodes, Ohm, num_proto = Number.prototype.__clone(), str_proto = String.prototype.__clone(); Node = function(uid) { var UID = uid; this.getUID = function() { return UID; }; }; Nodes = function() { var stack = []; this.add = function(id, val) { var n = new Node(stack.length); val.id = id; Object.extend(n, val); stack.push(n); return n.getUID(); }; this.getById = function(id) { return stack.filter(function(v) { var a = id || v.id; return (v.id === a); }); }; this.getByUID = function(UID) { return stack[UID]; }; this.get = function(callback) { !Object.isString(callback) || ( callback = [callback]); var f = Object.isFunction(callback) ? callback : (Object.isArray(callback) ? function(k) { return (callback.indexOf(k.id) >= 0); } : function(k) { return true; }); return stack.filter(f); }; }; Ohm = function(n) { var graph = n || (new Nodes()), filters = {}, __nodes = {}, addGS = function(obj, name, conf, binder) { var alfa = {}; Object.extend(alfa, conf); if (!alfa.get) { alfa.get = function() { var a = this.g.getById(this.p); return a.length === 1 ? a[0] : a; }.bind(binder); } else { alfa.get = alfa.get.bind(binder); } if (!alfa.set) { alfa.set = function(value) { this.g.getById(this.p).forEach(function(k) { Object.extend(k, value); return true; }); }.bind(binder); } else { alfa.set = alfa.set.bind(binder); } Object.defineProperty(obj, name, alfa); }, add = function(id, node) { if (__nodes.hasOwnProperty(id)) { addGS(__nodes, id, { enumerable : true }, { t : this, p : id, g : graph }); } return graph.add(id, node || {}); }; Object.extend(this, { add : function() { add.apply(this, arguments); }, map : function(name, f, that) { var n = name, filterer = ['add', 'map', '__all']; n = Object.isFunction(n) ? name.apply(that, arguments.slice(3)) : n; if (filterer.indexOf(n.toLowerCase()) >= 0) { console.log("You can't map over a basic property of object !!! Please read the freakin' manual."); return null; } if (!filters.hasOwnProperty(n)) { filters[n] = new Ohm(graph); addGS(this, n, { get : function() { this.g.get(this.f).forEach(function(v, key, arr) { var temp, binder; if (arr.length !== 1) { if (!this.filt.hasOwnProperty(v.id)) { addGS(this.filt, v.id, { set : function(value) { this.tggetById(this.p).filter(this.tf).forEach(function(k) { Object.extend(k, value); }); }, get : function() { var a = this.tggetById(this.p).filter(this.tf); return a.length === 1 ? a[0] : a; } }, { t : this, p : v.id }); (key !== arr.length - 1) || Object.extend(this.filt, this.g.get(this.f)); } } else { if (Object.isFunction(v.__new__)) { v.__default = function() { return Object.extend((new this.__new__(arguments)), this); }; } if (!Object.isUndefined(v.__default)) { temp = this.filt; this.filt = Object.isFunction(v.__default) ? v.__default.bind(v) : v.__default; if (Object.isNumber(this.filt) || Object.isString(this.filt)) { var prot = Object.isNumber(this.filt) ? Number : String; for (var i in temp) { if (temp.hasOwnProperty(i) && !prot.prototype.hasOwnProperty(i)) { var bin = { t : temp, m : i, p : prot, }; Object.defineProperty(prot.prototype, i, { set : function(value) { Object.defineProperty(this.p.prototype, this.m, { configurable : true,
And here is the demo:
var c = new Ξ©(); c.add('ann', { __default : 58, blah : 98, ceva : function() { console.log('asd'); } }); c.add('ann2',{ __default: function(){ console.log('hello'); }, abc: 78, dce: function(){ console.log(' world'); } }; c.add('b2', { __new__ : function() { this.init = function() { this.id = 86; }; this.mer = function() { console.log(this); }; }, els : 'asadar' }); c.map('b2', function(k) { return k.id === 'b2'; }); c.map('ann', function(k) { return k.id === 'ann'; }); c.map('ann2', function(k) { return k.id === 'ann2'; }); console.log(c.ann);
This code works, but the part in which I have to use the prototype Number or String bothers me. Do you have another way to do this?
The reason is to do something that someone said that it can be done in PHP but not JS, this guy recently worked with me in WebGL shaders and hated that he had to write 700 lines of code to use several effects combined with FBO instead of 100, which would take from him a similar tool like the one that was written in PHP. So, yes, I know that accessors on primitive prototypes are a hack, but how can I do it differently without using valueOf if the final object of the chain is primitive?