How to Extend javascript object methods - javascript

I have this object
var X = {
a:function(args){
},
b:function(args){
},
c: ...... etc
}
X.a() // will do somthing
I want to modify the method that it will do one more statement say "counter++"
So
var oldX = X.a();
X.a =function(args){
oldX(args);
counter++;
}
if i want to make this change to all the methods of the object X . So how can i do this by looping through the object like?
for(var i in X){
}
when i tried it by storing in an ooldArray of methods but while executing it will replace all the method with lastone ..

I'm completely sure the behavior you want should be achieved using prototypical inheritance. But I'm know nothing about your architecture and requirements, so here is the solution written in the way you want:
var x = {
a: function(){
console.log('x.a')
},
b: function(){
console.log('x.b')
}
},
oldx = {},
key;
for (key in x) {
oldx[key] = x[key];
(function(key){
x[key] = function(){
oldx[key].apply(this, arguments);
console.log('x.new_'+key);
}
})(key);
}
x.a();
x.b();

Here is what you want:
var x = {
a:function(){
return "this is a";
},
b:function(){
return "this is b";
},
c:function(){
return "this is c";
}
}
for(var abc in x)
{
alert(abc);
}

Related

Better way to update an object's value at a variable depth

I am working on some software that reads/writes information in localStorage using a handler. You can find a working example here: http://jsbin.com/wifucugoko/edit?js,console
My problem is with the segment of code below (focusing on the switch statement):
_t.set = function(path, value) { // Update a single value or object
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1);
switch (q.length) {
// There has to be a better way to do this...
case 1:
p[q[0]] = value;
break;
case 2:
p[q[0]][q[1]] = value;
break;
case 3:
p[q[0]][q[1]][q[2]] = value;
break;
case 4:
p[q[0]][q[1]][q[2]][q[3]] = value;
break;
case 5:
p[q[0]][q[1]][q[2]][q[3]][q[4]] = value;
break;
case 6:
p[q[0]][q[1]][q[2]][q[3]][q[4]][q[5]] = value;
break;
default:
return "error";
}
b.setItem(o, JSON.stringify(p));
return p;
} else {
b.setItem(path, JSON.stringify(value));
return this.get(path);
}
};
I am not going to be the only one using this codebase, and I am trying to make it easy for others to update any value that could be placed in localStorage. Right now you can update a value by using something like local.set('item.subitem.proeprty', 'value') Though the code above does that, it's ugly and doesn't scale.
How can this method be improved to (1) update a property nested at any depth automatically, instead of writing an infinitely-long switch statement, and (2) not lace a parent object with [object Object] after a value is updated?
This question has nothing to do with my use of localStorage. I originally posted this question in code review, which requires a working contextual example. They closed this question immediately, since part of my problem is the code I provided doesn't work once you start dealing with updating a value nested more than six objects deep. Though I could have continued my switch statement indefinitely, that's exactly what I'm trying to avoid.
With the three examples provided you'll see that setting a value in one place doesn't remove values in other places:
local.set('user.session.timeout', false);
local.set('user.name', {first:'john', last:'doe', mi:'c'});
local.set('user.PIN', 8675309);
All these values, though set at different times, only UPDATE or create a value, they do NOT clear any pre-existing values elsewhere.
As for me the minimal optimization would be following:
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1),
dist = p;
q.forEach(function(item, index) {
if (index < q.length - 1) {
dist = dist[item];
} else {
dist[item] = value;
}
});
b.setItem(o, JSON.stringify(p));
return p;
} else {
changed parts:
dist variable is created
hardcoded switch is replaced with foreach
You could try something like this, if the path does not exists, the value is null:
function retreiveValueFromObject(path, object) {
var pathList = path.split(".");
var currentValue = object;
var brokeEarly = false;
for (var i = 0; i < pathList.length; i++) {
if (currentValue[pathList[i]]) {
currentValue = currentValue[pathList[i]];
} else {
brokeEarly = true;
break;
}
}
return {
value: currentValue,
brokeEarly: brokeEarly
};
}
function setValueInObject(path, value, object) {
var nestedObj = retreiveValueFromObject(path, object).value;
var pathList = path.split(".");
var finalKey = pathList[pathList.length - 1];
nestedObj[finalKey] = value;
}
var someObject = {
a: {
c: {
d: "value"
},
z: "c"
},
b: {
f: {
x: "world"
},
g: "hello"
},
};
console.log(retreiveValueFromObject("b.f.x", someObject));
setValueInObject("b.f.y", "newValue", someObject);
console.log(someObject);
What you are looking for is a little bit of recursion, I just implemented the update method.
let localStorageHandler = function() {
let b = window.localStorage,
_t = this;
_t.get = function(a) {
try {
return JSON.parse(b.getItem(a))
} catch (c) {
return b.getItem(a)
}
};
function descendAndUpdate(obj, path, value) {
let current = path[0],
remainingPath = path.slice(1);
// found and update
if (obj.hasOwnProperty(current) && remainingPath.length === 0) {
obj[current] = value;
// found but still not there
} else if (obj.hasOwnProperty(current)) {
return descendAndUpdate(obj[current], remainingPath, value);
}
// if you want do add new properties use:
// obj[current] = value;
// in the else clause
else {
throw('can not update unknown property');
}
}
_t.set = function(path, value) { // Update a single value or object
if (~path.indexOf(".")) {
let o = path.split(".")[0],
p = this.get(o),
q = path.split(".").slice(1);
descendAndUpdate(p, q, value);
console.log(p);
b.setItem(o, JSON.stringify(p));
return p;
} else {
b.setItem(path, JSON.stringify(value));
return this.get(path);
}
};
_t.remove = function(a) { // removes a single object from localstorage
let c = !1;
a = "number" === typeof a ? this.key(a) : a;
a in b && (c = !0, b.removeItem(a));
return c
};
};
let local = new localStorageHandler();
// Create user and session info if it doesn't exist
let blankUser = new Object({
alias: '',
dob: '',
PIN: '',
level: 0,
name: {
first: '',
last: '',
mi:'',
},
session: {
token: '',
timeout: true,
lastChange: Date.now()
}
});
local.remove('user');
// Loads user data into localstorage
if (!local.get('user')) {
local.set('user', blankUser);
}
local.set('user.session.timeout', false);
local.set('user.name', {first:'john', last:'doe', mi:'c'});
local.set('user.PIN', 8675309);
// new property
// local.set('user.sunshine', { 'like': 'always' });
console.log(local.get('user'));
A friend of mine would always prefer stacks over recursion, which would be a second option. Anyway I agree with many of the comments here. You already know your domain model. Unless you have a very good reason for this approach spend more time on serializing and unserializing those objects in the database. I have the impression you would be able to work with your data in a more natural way because the aspect of updating fields in a database would be abstracted away.
I am working on a similar project at the moment. What I am doing is storing the data in something I called a WordMatrix (https://github.com/SamM/Rephrase/blob/master/WordMatrix.js), maybe you could use something like it in your solution.
My project is a WIP but the next step is literally to add support for localStorage. The project itself is a database editor that works with key => value stores. You can view the prototype for it here: (https://samm.github.io/Rephrase/editor.html)
Once I have implemented the localStorage aspect I will update this post.
Your topic reminds me one recent another topic.
Trying to enhance the answer I provided, I propose you these functions:
// Function to get a nested element:
function obj_get_path(obj, path) {
return path.split('.').reduce((accu, val) => accu[val] || 'Not found', obj);
}
// Function to set a nested element:
function obj_set_path(obj, path, value) {
var result = obj;
var paths = path.split('.');
var len = paths.length;
for (var i = 0; i < len - 1; i++) {
result = result[paths[i]] || {};
}
result[paths[len - 1]] = value;
return obj;
}
// Example object
var obj = {
name0: 'A name',
level0: {
name1: 'An other name',
level1: {
level2: {
name3: 'Name to be changed',
text3: 'Some other text'
}
}
}
}
// Use of the function
obj = obj_set_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_set_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_get_path(obj, 'level0.level1.level2');
// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct
⋅
⋅
⋅
We could also adapt the 2nd function in my above snippet so that it works for both get and set, depending of if "value" is set:
// We could also adapt the second function for both uses:
function obj_path(obj, path, value = null) {
var result = obj;
var paths = path.split('.');
var len = paths.length;
for (var i = 0; i < len - 1; i++) {
result = result[paths[i]] || {};
}
// Return result if there is no set value
if (value === null) return result[paths[len - 1]];
// Set value and return
result[paths[len - 1]] = value;
return obj;
}
// Example object
var obj = {
name0: 'A name',
level0: {
name1: 'An other name',
level1: {
level2: {
name3: 'Name to be changed',
text3: 'Some other text'
}
}
}
}
// Use of the function
obj = obj_path(obj, 'level0.level1.level2.name3', 'Takit Isy');
obj = obj_path(obj, 'level0.level1.level2.new3', 'I’m a new element!');
var obj_level2 = obj_path(obj, 'level0.level1.level2');
// Consoling
console.log('Consoling of obj_level2:\n', obj_level2);
console.log('\nConsoling of full obj:\n', obj); // To see that the object is correct
Hope it helps.
How about:
function parse(str) {
var arr = str.split('.');
return function(obj) {
return arr.reduce((o, i) => o[i], obj);
}
}
let foo = {
a: {
b: {
c: {
bar: 0
}
}
}
}
let c = parse('a.b.c')(foo);
console.log(c.bar);
c['bar'] = 1;
console.log(foo);

Javascript object descriptors

Is it possible to use getters/setters on Javascript objects like the following basic functionality mockup?
function Descriptor(x) {
this._value = x;
this.setter = function(x) {
// Set internal value after some action or modification
this._value = x + 1;
}
this.getter = function() {
// Return the internal value
return this._value;
}
}
var obj = {};
obj.a = new Descriptor();
obj.a = 5; // Would run the setter defined in the Descriptor object
obj.a == 6; // Should evaluate to true in this trivial example
// Same as above, just an example of being able to easily reuse the getter/setter
obj.b = new Descriptor();
obj.b = 10;
obj.b == 11;
Ultimately, it should operate similarly to a Python descriptor set in a class definition. The only things I can find that accomplish something like this requires that the getter/setter be hooked up during the above obj creation, and cannot be easily reused on multiple attributes or objects.
You can try ES5 Object.defineProperty:
function addDescriptor(obj, prop) {
var value = 0;
Object.defineProperty(obj, prop, {
get: function(x) {
return value;
},
set: function(x) {
value = x + 1;
}
});
}
var obj = {};
addDescriptor(obj, 'a');
obj.a = 5;
obj.a == 6; // true
addDescriptor(obj, 'b');
obj.b = 10;
obj.b == 11; // true
I don't know what your environment is (it's only tagged javascript), but ES6 Proxies provide this flexibility of get/set
copied from MDN:
var handler = {
get: function(target, name){
return name in target?
target[name] :
37;
}
};
var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;
console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37
You can either use Object.defineProperty to define getter and setter, or simply do it in an object initialiser:
var obj = {
value: 0,
get a(){
return this.value;
},
set a(x){
this.value = x + 1;
}
};
I believe you are trying to do something like this: (Another way of doing what Oriol has done in his answer.)
Object.prototype.Descriptor = function(name){
var value = 0;
Object.defineProperty(this, name, {
get: function(){ return value; },
set: function(x){ value = x + 1; }
});
};
var obj = {};
obj.Descriptor("a");
obj.Descriptor("b");
obj.a = 5;
obj.b = 10;
obj.a //6
obj.b //11
obj.value //undefined
Everything is separated, such that the function can be reused meanwhile values are kept separated from all other object's value.

jQuery extend multiple objects with the same function

So I'm using $.extend to combine multiple components (objects). Some of these components have a function with the same key. I want the final extended object to have that same key, but have it point to a function which calls all of the merged components' versions of the functions one after the other.
So it'd look something like this:
var a = { foo: function() { console.log("a"); } };
var b = { foo: function() { console.log("b"); } };
var c = {}; // Doesn't have foo
var d = $.extend({}, a, b, c);
var e = $.extend({}, a, c);
var f = $.extend({}, c);
d.foo(); // Should call function() { console.log("a"); console.log("b"); }
e.foo(); // Should call function() { console.log("a"); }
f.foo(); // Should call function() {}
Is there a pragmatic way of doing this? I only want to do this for a specific set of keys, so I would only want to merge those specific keys' functions together and let the ordering in extend overwrite anything else.
Hopefully that makes sense :S
Note
f.foo(); // Should call function() {}
object c does not appear to have property foo . callling f.foo() returns TypeError: undefined is not a function . Not certain if requirement to add foo function to extended f object , or return object c (empty object) from anonymous function ? At piece below , foo function not added to extended f object.
jquery $.Callbacks() utilized to add functions having foo property at $.each()
Try
var a = { foo: function() { console.log("a"); } };
var b = { foo: function() { console.log("b"); } };
var c = {}; // Doesn't have foo
//d.foo();
// Should call function() { console.log("a"); console.log("b"); }
//e.foo();
// Should call function() { console.log("a"); }
//f.foo();
// Should call function() {}
var callbacks = $.Callbacks();
var arr = [], d, e, f;
$.each([a,b,c], function(k, v, j) {
var j = [a,b,c];
// filter objects having `foo` property
if (v.hasOwnProperty("foo")) {
arr.push([v, v.foo]);
if (arr.length > 1) {
callbacks.add(arr[0][1], arr[1][1]);
// `add` `foo` properties to `callbacks`
// `fire` both `callbacks` when `object.foo` called
j[k -1].foo = callbacks.fire;
d = $.extend({}, j[k - 1])
} else {
// `else` extend original data (`fn`, `object`)
// contained within object
e = $.extend({}, j[k + 1]);
f = $.extend({}, j[++k + 1]);
}
}
});
d.foo(); // `a` , `b`
e.foo(); // `b`
console.log(f); // `Object {}`
f.foo() // `TypeError: undefined is not a function`
jsfiddle http://jsfiddle.net/guest271314/3k35buc1/
See jQuery.Callbacks()
Here's what I ended up with based off of guest271314's answer. toMix is the array of components to be mixed into the object. I actually didn't need the dummy functions I thought I might, and I ended up using an array of functions instead of the $.Callbacks() so that I could control the order in which the functions are called. I also needed to use the call() function so that I could call the functions from the correct this object.
this.functionMerge = function(toMix) {
var callbacks = {};
var functions = {};
var obj = {};
var keys = [
'componentWillMount',
'componentDidMount',
'componentWillUpdate',
'componentDidUpdate',
'componentWillUnmount'
]
$.each(keys, function(key, value) {
callbacks[value] = [];
});
for (i = 0; i < toMix.length; ++i) {
$.each(keys, function(key, value) {
if (toMix[i].hasOwnProperty(value) && typeof toMix[i][value] == 'function') {
callbacks[value].push(toMix[i][value]);
}
});
$.extend(true, obj, toMix[i]);
}
$.each(keys, function(key, value) {
functions[value] = function() {
var that = this;
$.each(callbacks[value], function(key, value) {
value.call(that);
});
};
});
return $.extend(true, obj, functions);
}

Setting properties on functions in JavaScript object literal notation

var api = {};
api.c = function () {return 1};
api.c.m = function () {return 2};
alert(api.c()); // returns 1
alert(api.c.m()); // returns 2
var api2 = {
c: function () {}; // can't put m inside c in object literal notation
};
How would we embed m in c in object literal notation?
You can't. However, you could do
Object.defineProperty(api.c, 'm', { value: function() { return 2; } });
Since Object.defineProperty returns the object, you could do
var api = {
c: Object.defineProperty(function() { }, 'm', {
value: function() { return 2; }
})
};
Or for multiple properties:
var api = {
c: Object.defineProperties(function() { }, {
m: { value: function() { return 2; } },
...
})
};
This may come closest to satisfying your desire to write the function properties in object literal form.
Or, you could use the extend feature available in most frameworks (or Object.assign in ES6):
var api = {
c: Object.assign(function() { }, {
m: function() { return 2; }
)
};
Feel free to replace Object.assign with $.extend, _.extend, etc.
Depending on your tolerance for ugliness, you might try the following, a variation on #zerkms's proposal without the IIFE (you'll need a variable x):
var api = {
c: (x = function() { }, x.m = function() { return 2; }, x)
};
It technically is possible, but it's ugly
var api2 = {
c: (function() {
var f = function() {};
f.m = 'something else';
return f;
}())
};
So I personally don't see a good reason to do it that way instead of how you do it in the 1st case.

Set length property of JavaScript object

Let's say I have a JavaScript object:
function a(){
var A = [];
this.length = function(){
return A.length;
};
this.add = function(x){
A.push(x);
};
this.remove = function(){
return A.pop();
};
};
I can use it like so:
var x = new a();
x.add(3);
x.add(4);
alert(x.length()); // 2
alert(x.remove()); // 4
alert(x.length()); // 1
I was trying to make .length not a function, so I could access it like this: x.length, but I've had no luck in getting this to work.
I tried this, but it outputs 0, because that's the length of A at the time:
function a(){
var A = [];
this.length = A.length;
//rest of the function...
};
I also tried this, and it also outputs 0:
function a(){
var A = [];
this.length = function(){
return A.length;
}();
//rest of the function...
};
How do I get x.length to output the correct length of the array inside in the object?
You could use the valueOf hack:
this.length = {
'valueOf': function (){
return A.length;
},
'toString': function (){
return A.length;
}
};
Now you can access the length as x.length. (Although, maybe it's just me, but to me, something about this method feels very roundabout, and it's easy enough to go with a sturdier solution and, for example, update the length property after every modification.)
If you want A to stay 'private', you need to update the public length property on every operation which modifies A's length so that you don't need a method which checks when asked. I would do so via 'private' method.
Code:
var a = function(){
var instance, A, updateLength;
instance = this;
A = [];
this.length = 0;
updateLength = function()
{
instance.length = A.length;
}
this.add = function(x){
A.push(x);
updateLength();
};
this.remove = function(){
var popped = A.pop();
updateLength();
return popped;
};
};
Demo:
http://jsfiddle.net/JAAulde/VT4bb/
Because when you call a.length, you're returning a function. In order to return the output you have to actually invoke the function, i.e.: a.length().
As an aside, if you don't want to have the length property be a function but the actual value, you will need to modify your object to return the property.
function a() {
var A = [];
this.length = 0;
this.add = function(x) {
A.push(x);
this.length = A.length;
};
this.remove = function() {
var removed = A.pop();
this.length = A.length;
return removed;
};
};
While what everyone has said is true about ES3, that length must be a function (otherwise it's value will remain static, unless you hack it to be otherwise), you can have what you want in ES5 (try this in chrome for example):
function a(){
var A = [],
newA = {
get length(){ return A.length;}
};
newA.add = function(x){
A.push(x);
};
newA.remove = function(){
return A.pop();
};
return newA;
}
var x = a();
x.add(3);
x.add(4);
alert(x.length); // 2
alert(x.remove()); // 4
alert(x.length); // 1
You should probably use Object.create instead of the function a, although I've left it as a function to look like your original.
I don't think you can access it as a variable as a variable to my knoledge cannot return the value of a method, unless you will hijack the array object and start hacking in an update of your variable when the push/pop methods are called (ugly!). In order to make your method version work I think you should do the following:
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};
These days you can use defineProperty:
let x = {}
Object.defineProperty(x, 'length', {
get() {
return Object.keys(this).length
},
})
x.length // 0
x.foo = 'bar'
x.length // 1
Or in your specific case:
Object.defineProperty(x, 'length', {
get() {
return A.length
}
})
function a(){
this.A = [];
this.length = function(){
return this.A.length;
};
this.add = function(x){
this.A.push(x);
};
this.remove = function(){
return this.A.pop();
};
};

Categories

Resources