I have an app that allows users to generate objects, and store them (in a MySQL table, as strings) for later use. The object could be :
function Obj() {
this.label = "new object";
}
Obj.prototype.setLabel = function(newLabel) {
this.label = newLabel;
}
If I use JSON.stringify on this object, I will only get the information on Obj.label (the stringified object would be a string like {label: "new object"}. If I store this string, and want to allow my user to retrieve the object later, the setLabel method will be lost.
So my question is: how can I re-instantiate the object, so that it keeps the properties stored thanks to JSON.stringify, but also gets back the different methods that should belong to its prototype. How would you do that ? I was thinking of something along "create a blank object" and "merge it with the stored one's properties", but I can't get it to work.
To do this, you'll want to use a "reviver" function when parsing the JSON string (and a "replacer" function or a toJSON function on your constructor's prototype when creating it). See Section 15.12.2 and 15.12.3 of the specification. If your environment doesn't yet support native JSON parsing, you can use one of Crockford's parsers (Crockford being the inventor of JSON), which also support "reviver" functions.
Here's a simple bespoke example that works with ES5-compliant browsers (or libraries that emulate ES5 behavior) (live copy, run in Chrome or Firefox or similar), but look after the example for a more generalized solution.
// Our constructor function
function Foo(val) {
this.value = val;
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.toJSON = function() {
return "/Foo(" + this.value + ")/";
};
// An object with a property, `foo`, referencing an instance
// created by that constructor function, and another `bar`
// which is just a string
var obj = {
foo: new Foo(42),
bar: "I'm bar"
};
// Use it
display("obj.foo.value = " + obj.foo.value);
display("obj.foo.nifty = " + obj.foo.nifty);
display("obj.bar = " + obj.bar);
// Stringify it with a replacer:
var str = JSON.stringify(obj);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var obj2 = JSON.parse(str, function(key, value) {
if (typeof value === "string" &&
value.substring(0, 5) === "/Foo(" &&
value.substr(-2) == ")/"
) {
return new Foo(value.substring(5, value.length - 2));
}
return value;
});
// Use the result
display("obj2.foo.value = " + obj2.foo.value);
display("obj2.foo.nifty = " + obj2.foo.nifty);
display("obj2.bar = " + obj2.bar);
Note the toJSON on Foo.prototype, and the function we pass into JSON.parse.
The problem there, though, is that the reviver is tightly coupled to the Foo constructor. Instead, you can adopt a generic framework in your code, where any constructor function can support a fromJSON (or similar) function, and you can use just one generalized reviver.
Here's an example of a generalized reviver that looks for a ctor property and a data property, and calls ctor.fromJSON if found, passing in the full value it received (live example):
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
if (typeof value === "object" &&
typeof value.ctor === "string" &&
typeof value.data !== "undefined") {
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
if (typeof ctor === "function" &&
typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
}
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
To avoid having to repeat common logic in toJSON and fromJSON functions, you could have generic versions:
// A generic "toJSON" function that creates the data expected
// by Reviver.
// `ctorName` The name of the constructor to use to revive it
// `obj` The object being serialized
// `keys` (Optional) Array of the properties to serialize,
// if not given then all of the objects "own" properties
// that don't have function values will be serialized.
// (Note: If you list a property in `keys`, it will be serialized
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, index, key;
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
for (index = 0; index < keys.length; ++index) {
key = keys[index];
data[key] = obj[key];
}
return {ctor: ctorName, data: data};
}
// A generic "fromJSON" function for use with Reviver: Just calls the
// constructor function with no arguments, then applies all of the
// key/value pairs from the raw data to the instance. Only useful for
// constructors that can be reasonably called without arguments!
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
obj[name] = data[name];
}
return obj;
}
The advantage here being that you defer to the implementation of a specific "type" (for lack of a better term) for how it serializes and deserializes. So you might have a "type" that just uses the generics:
// `Foo` is a constructor function that integrates with Reviver
// but doesn't need anything but the generic handling.
function Foo() {
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.spiffy = "I'm the spiffy inherited property.";
Foo.prototype.toJSON = function() {
return Generic_toJSON("Foo", this);
};
Foo.fromJSON = function(value) {
return Generic_fromJSON(Foo, value.data);
};
Reviver.constructors.Foo = Foo;
...or one that, for whatever reason, has to do something more custom:
// `Bar` is a constructor function that integrates with Reviver
// but has its own custom JSON handling for whatever reason.
function Bar(value, count) {
this.value = value;
this.count = count;
}
Bar.prototype.nifty = "I'm the nifty inherited property.";
Bar.prototype.spiffy = "I'm the spiffy inherited property.";
Bar.prototype.toJSON = function() {
// Bar's custom handling *only* serializes the `value` property
// and the `spiffy` or `nifty` props if necessary.
var rv = {
ctor: "Bar",
data: {
value: this.value,
count: this.count
}
};
if (this.hasOwnProperty("nifty")) {
rv.data.nifty = this.nifty;
}
if (this.hasOwnProperty("spiffy")) {
rv.data.spiffy = this.spiffy;
}
return rv;
};
Bar.fromJSON = function(value) {
// Again custom handling, for whatever reason Bar doesn't
// want to serialize/deserialize properties it doesn't know
// about.
var d = value.data;
b = new Bar(d.value, d.count);
if (d.spiffy) {
b.spiffy = d.spiffy;
}
if (d.nifty) {
b.nifty = d.nifty;
}
return b;
};
Reviver.constructors.Bar = Bar;
And here's how we might test that Foo and Bar work as expected (live copy):
// An object with `foo` and `bar` properties:
var before = {
foo: new Foo(),
bar: new Bar("testing", 42)
};
before.foo.custom = "I'm a custom property";
before.foo.nifty = "Updated nifty";
before.bar.custom = "I'm a custom property"; // Won't get serialized!
before.bar.spiffy = "Updated spiffy";
// Use it
display("before.foo.nifty = " + before.foo.nifty);
display("before.foo.spiffy = " + before.foo.spiffy);
display("before.foo.custom = " + before.foo.custom + " (" + typeof before.foo.custom + ")");
display("before.bar.value = " + before.bar.value + " (" + typeof before.bar.value + ")");
display("before.bar.count = " + before.bar.count + " (" + typeof before.bar.count + ")");
display("before.bar.nifty = " + before.bar.nifty);
display("before.bar.spiffy = " + before.bar.spiffy);
display("before.bar.custom = " + before.bar.custom + " (" + typeof before.bar.custom + ")");
// Stringify it with a replacer:
var str = JSON.stringify(before);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var after = JSON.parse(str, Reviver);
// Use the result
display("after.foo.nifty = " + after.foo.nifty);
display("after.foo.spiffy = " + after.foo.spiffy);
display("after.foo.custom = " + after.foo.custom + " (" + typeof after.foo.custom + ")");
display("after.bar.value = " + after.bar.value + " (" + typeof after.bar.value + ")");
display("after.bar.count = " + after.bar.count + " (" + typeof after.bar.count + ")");
display("after.bar.nifty = " + after.bar.nifty);
display("after.bar.spiffy = " + after.bar.spiffy);
display("after.bar.custom = " + after.bar.custom + " (" + typeof after.bar.custom + ")");
display("(Note that after.bar.custom is undefined because <code>Bar</code> specifically leaves it out.)");
You can indeed create an empty instance and then merge the instance with the data. I recommend using a library function for ease of use (like jQuery.extend).
You had some errors though (function ... = function(...), and JSON requires keys to be surrounded by ").
http://jsfiddle.net/sc8NU/1/
var data = '{"label": "new object"}'; // JSON
var inst = new Obj; // empty instance
jQuery.extend(inst, JSON.parse(data)); // merge
Note that merging like this sets properties directly, so if setLabel is doing some checking stuff, this won't be done this way.
So far as I know, this means moving away from JSON; you're now customizing it, and so you take on all of the potential headaches that entails. The idea of JSON is to include data only, not code, to avoid all of the security problems that you get when you allow code to be included. Allowing code means that you have to use eval to run that code and eval is evil.
If you want to use the setters of Obj :
Obj.createFromJSON = function(json){
if(typeof json === "string") // if json is a string
json = JSON.parse(json); // we convert it to an object
var obj = new Obj(), setter; // we declare the object we will return
for(var key in json){ // for all properties
setter = "set"+key[0].toUpperCase()+key.substr(1); // we get the name of the setter for that property (e.g. : key=property => setter=setProperty
// following the OP's comment, we check if the setter exists :
if(setter in obj){
obj[setter](json[key]); // we call the setter
}
else{ // if not, we set it directly
obj[key] = json[key];
}
}
return obj; // we finally return the instance
};
This requires your class to have setters for all its properties.
This method is static, so you can use like this :
var instance = Obj.createFromJSON({"label":"MyLabel"});
var instance2 = Obj.createFromJSON('{"label":"MyLabel"}');
From ECMAScript 6 onwards you can just do:
Object.assign(new Obj(), JSON.parse(rawJsonString))
Note: You create a new empty object of the defined type first and then override its properties with the parsed JSON. Not the other way around.
The methods define behaviour and contain no variable data. They are "stored" as your code. So you don't actually have to store them in the database.
You would have to write your own stringify method that stores functions as properties by converting them to strings using the toString method.
JavaScript is prototype based programming language which is classless language where object orientation achieved by process of cloning existing objects that serve as prototypes.
Serializing JSON would be considering any methods, for instance if you have an object
var x = {
a: 4
getText: function() {
return x.a;
}
};
You will get just { a:4 } where getText method is skipped by the serializer.
I ran into this same trouble a year back and I had to maintain a separate helper class for each of my domain object and used $.extend() it to my deserialized object when need, just more like having methods to a base class for the domain objects.
Try to use toString on the method.
Update:
Iterate over the methods in obj and store them as string, and then instantiate them with new Function.
storedFunc = Obj.prototype.setLabel.toString();
Obj2.prototype['setLabel'] = new Function("return (" + storedFunc + ")")();
Related
// Extending the Storage object:
Storage.prototype.setObject = function(key, value) {
this.setItem(key, JSON.stringify(value));
}
Storage.prototype.getObject = function(key) {
var value = this.getItem(key);
return value && JSON.parse(value);
}
// START HERE:
var dog = {
name : '',
woof : function(){ console.log('woof woof, I am ' + this.name); }
}
dog.name = 'Lovely';
//Store in the localstorage
localStorage.setObject('mydog', dog);
//Get from the localstorage
var mydog = localStorage.getObject('mydog');
console.log(mydog.name); // prints 'Lovely'
console.log(mydog.woof()); // ootest.html:36 Uncaught TypeError: mydog.woof is not a function(…)
Why I am getting that error ?
The right way: use object constructor
let Dog = function(name) {
this.name = name
}
Dog.prototype.woof = function woof() {
console.log('woof woof, I am ' + this.name);
}
// Create a 'static' method for a Dog 'class'
Dog.fromJSON = function fromJSON(json) {
return new Dog(JSON.parse(json).name)
}
// Instantinate your objects with `new` keyword
let dog = new Dog('Lovely')
dog.woof()
let storedDog = JSON.stringify(dog)
console.log('stored:', storedDog)
// Dog.fromJSON() returns new Dog object,
// with the same parameters for a constructor
// and with all methods
let restoredDog = Dog.fromJSON(storedDog)
restoredDog.woof()
Limitation
This approach will work well only for objects than will behave similar if created with similar constructor parameter. If you want to store objects with rich data inside, please refer to How come JSON can't save object's functions? accepted answer.
Just for learning something new: storing functions in JSON
Functions can be created from string at runtime via Function object. To create function it we need to pass arguments and body:
new Function ([arg1[, arg2[, ...argN]],] functionBody)
To get a method params and body we need to call inherited toString() method on method:
dog.woof.toString()
We can store function declaration in string and we can create function from string. Next steps are: implement conventional functions storage as object properties, implement restoring saved functions from JSON string.
Dirty working implementation example in the snippet below.
Why you should not implement this?
Security risk. Somebody could hack the serialised functions with any arbitrary code.
Bad code design. Having objects without predefined constructors leads to maintenance hell, because you can't you javascript duck typing to make assumptions about object behavior.
Versioning. If you update you code, you can't be sure about which version of objects stored on clients.
let dog = {
name : '',
woof : function() {
console.log('woof woof, I am ' + this.name);
}
}
dog.name = 'Lovely';
dog.woof()
let storedDog = saveWithFuncToJSON(dog)
console.log('stored:', storedDog)
let restoredDog = restoreWithFuncFromJSON(storedDog)
restoredDog.woof()
console.log("Don't use this approach in production!")
// Code below is created only for fun,
// if you need this code in production,
// then your code smells
// Return JSON string
function saveWithFuncToJSON(object) {
// Use Object.assign() just for simplicity,
// something more solid needed for real object copying
let preparedObject = Object.assign({}, object)
for (let prop in preparedObject) {
if (typeof(preparedObject[prop]) !== 'function') continue
// Different platforms constructing function string in different ways
// so you'll have to put a lot of efforts to make it work stable
let funcStr = preparedObject[prop].toString()
let startParams = funcStr.indexOf('(') + 1
let endParams = funcStr.indexOf(')')
let hasParams = (endParams - startParams)
let funcParams = !hasParams ? [] : funcStr.slice(
funcStr.indexOf('(') + 1,
funcStr.indexOf('\n')
).split(',')
let funcBody = funcStr.slice(
funcStr.indexOf('{') + 1,
funcStr.lastIndexOf('}')
)
// This is the most interesting part
// We will store function as a string like freezing humans
preparedObject[`__${prop}Func`] = {
params: funcParams,
body: funcBody
}
}
return JSON.stringify(preparedObject)
}
function restoreWithFuncFromJSON(jsonSting) {
let object = JSON.parse(jsonSting)
for (let prop in object) {
// Functions to be restored should be named differently
let shouldConvertToFunc = prop.startsWith('__') && prop.endsWith('Func')
if (!shouldConvertToFunc) continue
let funcName = prop.slice(2, -4)
let funcData = object[prop]
let contructorArgsArray = funcData.params.concat([funcData.body])
// Function() does all work for us
object[funcName] = Function.apply(null, contructorArgsArray)
delete object[prop]
}
return object;
}
LocalStorage only supports strings. You can store objects with JSON-encoding, but that won't work for your functions.
JSON only supports numbers, strings, booleans, arrays, and basic object structures.
I have seen examples of how to do this that rely heavily on the browser environment but not examples that work in the native node.js environment.
I need to cast JSON objects to javascript classes of a type that does not yet exist but is given by an input string.
I found some nice code on stackoverflow to retype JSON to known class but I have not figured out how to do this when the class type is a string and the class does not exist.
In software terms I need to:
var className = 'Bar';
console.log(global[className]); // false - class Bar is not defined
var jsonIn = '{ "name": "Jason" }';
var retypedJson = retypeJSON(jsonIn, className);
console.log(retypedJson instanceof Bar) // true
The code for recasting JSON. (Nice as it doesn't call eval or explicitly copy property names.)
// define a class
var Foo = function(name) {
this.name = name;
}
// make a method
Foo.prototype.shout = function() {
console.log("I am " + this.name);
}
// make a simple object from JSON:
var x = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
x.__proto__ = Foo.prototype;
// the method works
x.shout();
console.log(x instanceof Foo); // true
Thanks!
I have an answer that makes some use of eval and __proto__. Eval is only used to create the first prototype.
// create prototype - called once
var on = 'Apple';
var estr = 'function ' + on + '() {} ' + on + '.prototype.getInfo = function() { return this.name; }; ';
eval.apply(global, [estr]);
// make a simple object from JSON:
// called many times
var apl = JSON.parse('{ "name": "Jason" }');
// force its class to be Foo
apl.__proto__ = global[on].prototype;
// this method works
console.log(apl.getInfo());
Maybe this?
var constructors = {
"Obj1": ObjConstructor1(){},
"Obj2": ObjConstructor2(){},
}
var obj = new constructors.Obj1();
var jsonObj = {"a":1,"b":2};
for (var x in jsonObj) {
obj[x] = jsonObj[x];
}
I am trying you get a better understanding of JavaScript, especially the prototype functionality. I am having trouble with this case:
I am trying to define a function someObject with a type function so that it will behave like the following:
var myTestObject = someObject();
If I call:
myTestObject() ===> "The object is initailType"
and then when this is called
myTestObject.type() ===> "InitialType"
Then if I make this call
myTestObject.type("newtype")
myTestObject.type() ===> "newType"
A call to
myTestObject() ===> "The Object is newType".
I have tried both this How does JavaScript .prototype work?
and this How do you create a method for a custom object in JavaScript?
,but I am getting several different errors depending on how it is implemented, mostly this though (Uncaught TypeError: Object myTestObject has no method 'type'). I feel like I am making this harder then it should be.
edit: more code.
function box(){
var _current = "initialType"
Object.defineProperty(this, "current", {
get: function(){return _current;},
set: function(value){
if(arguments.length === 1){
_current = value;
} }
})
return "The Object is " + this.type(this.current)
}
box.prototype.type = function(newValue){
var type = null;
if(arguments.length == 0){
type = "initialType";
}else {
type = newValue
}
return type
}
I would use something like this:
function Box(){}
Box.prototype.type = "initialType";
Box.prototype.toString = function() {
return "The Object is " + this.type + ".";
};
And use it like this:
var b = new Box();
b.type; // "initialType"
b + ''; // "The Object is initialType."
b.type = 'otherType'; // "otherType"
b.type; // "otherType"
b + ''; // "The Object is otherType."
This does what you've asked, but I don't understand what you want to do with the prototype, so this code doesn't use that. For example, the sample code doesn't use new, so the return value of someObject won't use its prototype.
function someObject()
{
var currentType = "initailType";
var formatter = function() {
return "The object is " + currentType;
};
formatter.type = function(value) {
if (arguments.length == 0) {
return currentType;
} else {
currentType = value;
}
};
return formatter;
}
var myTestObject = someObject();
myTestObject(); // => "The object is initailType"
myTestObject.type(); // => "initialType"
myTestObject.type("newType");
myTestObject.type(); // => "newType"
myTestObject(); // => "The object is newType".
see demo
Edit: example using prototype and new.
function Box() { // class name starts with a capital letter
this._type = "initialType"; // set up default values in constructor function
} // no "return" in constructor function, using "new" handles that
Box.prototype.type = function(value) { // adding method to the prototype
if (arguments.length == 0) { // magic arguments local variable...
return this._type; // initially returns the value set in the constructor
} else {
this._type = value; // update the stored value
}
};
Box.prototype.format = function() // another method on the box, rather than a return from the constructor
{
return "The object is " + this.type(); // could use this._type instead
};
var box = new Box(); // instance variable with lowercase name
console.log(box.type()); // read the default value
console.log(box.format()); // print the message with the initial value of type
box.type("another type"); // set the type property, no return value
console.log(box.format()); // print the new message
How could I get a callback whenever new properties are set on a Javascript Object..?
I.e. I don't know which properties are going to be set, but want a callback for any properties that are set.
What I want is
var obj = {};
obj.a = "something"; // triggers callback function
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
Is this possible?
You can use the new defineProperty:
function onChange(propertyName, newValue){
...
}
var o = {};
Object.defineProperty(o, "propertyName", {
get: function() {return pValue; },
set: function(newValue) { onChange("propertyName",newValue); pValue = newValue;}});
But depends on the browser version you need to support.
Edit: Added snippet on Jsfiddle, works in IE10. http://jsfiddle.net/r2wbR/
The best thing to do it is a setter function, you don't need a getter,
var obj = {};
setKey(obj, 'a', "something"); // triggers callback function
function setKey(obj, key, val){
obj[key] = val;
callback(obj, key, val);
}
function callback(obj, key, val) {
console.log(key + " was set to " + val + " on ", obj);
}
Do it as generic as you can, don't do a different function for all keys
Try it here
The best idea is to have setter and getter methods. But according to your previous implementation one could still alter your object properties without using setter and getter. Therfore you should make you senstive variables privat. Here is a brief example:
var Person = (function() {
function Person() {};
var _name;
Person.prototype.setName = function(name) {
_name = name;
};
Person.prototype.getName = function() {
return _name;
};
return Person;
})();
var john = new Person();
john.getName();
// => undefined
john.setName("John");
john.getName();
// => "John"
john._name;
// => undefined
john._name = "NOT John";
john.getName();
// => "John"
Unfortunately, JavaScript won't let you know when a property is changed. Many times I've wished it would, but since it wouldn't I've had to find a workaround. Instead of setting the property directly, set it via a setter method which triggers the callback function (and possible use a getter method to access the property too) like this:
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
var obj = {};
obj.setA=function(value){
obj.a=value;
callback(obj,'a',value);// triggers callback function
}
obj.getA=function(){
return obj.a;
}
obj.setA("something");
jsFiddle: http://jsfiddle.net/jdwire/v8sJt/
EDIT: Another option if you want to completely prevent changing the property without a callback:
function callback(obj,key,val) {
console.log(key + " was set to " + val + " on ", obj);
}
var obj={};
(function(){
var a=null;
obj.setA=function(value){
a=value;
callback(obj,'a',value);// triggers callback function
}
obj.getA=function(){
return a;
}
})()
console.log("a is "+obj.getA());// a is null
obj.setA("something"); // Set a to something
console.log("a is now "+obj.getA()); // a is now something
obj.a="something else"; // Set obj.a to something else to show how a is only accessible through setA
console.log("a is still "+obj.getA()); // a is still something
jsFiddle: http://jsfiddle.net/jdwire/wwaL2/
I'm in the process of writing a simple prototype in Node.js with some helper methods that I'll probably need in objects using that prototype. One method that I'd like to have is an implementation of jQuery's .each(). I've looked at jQuery's implementation in their development release, and tried to emulate it in my simplified version here.
// Loop through the object using a callback
BaseProto.prototype.each = function (cb, args) {
var obj = this.get(), // Get our object as a plain object
prop;
/** Code to make sure the the args passed are actually an array **/
if (typeof cb === "function") {
// For each property in our object
for (prop in obj) {
// Apply the callback to the object's property and pass
// the index, the property, and whatever else as arguments
if (cb.apply(obj[prop], [ prop, obj[prop] ].concat(args)) === false) {
// Get out if the function returns false
break;
}
}
}
// Reset our object with the new one
return this.reset(obj);
};
The problem is that while the callback is definitely being fired, it doesn't have any effect on the object's property. No matter what I do inside the callback, the changes stay inside the callback's scope.
Here is an example of a simple callback that I've been testing with.
var BaseProtoTestObj = new BaseProto();
/** Set some properties to BaseProtoTestObj **/
function cb1 ( key, val ) {
var prop;
key = key.toString() + " Callbacked";
val = val.toString() + " Callbacked";
for (prop in this) {
this[prop] = this[prop].toString() + " Callbacked";
}
}
// Doesn't have any effect on BaseProtoTestObj
BaseProtoTestObj.each(cb1);
I can see that there is a lot more going on in jQuery's .each(), but from what I can gather it's for optimization and the ability to iterate over arrays as well as objects.
In the end, my question is simple. What is it that jQuery is doing to affect the properties, that I'm not in my .each()?
Edit
I suppose another question to ask would be if my logic is fundamentally wrong, and you can't modify an object's properties this way.
You don't need a custom method:
for(var prop in object) {
var value = object[prop];
// do something with value and/or prop
}
Although if you really needed .each(), you could do something like this:
Object.prototype.each = function(cb) {
for(var propName in this) {
cb(propName, this[propName]);
}
}
var foo = { prop: 'value', prop2: 'value2' };
foo.each(function(key,value) {
// do something here
});
Since you need to modify the actual values of the properties, try this:
Object.prototype.mutate = function(cb) {
for(var propName in this) {
this[propName] = cb(propName, this[propName]);
}
}
var obj = {
a: 'foo',
b: 'bar',
c: 'baz'
};
obj.mutate(function(propName, propValue) {
return propName + '-' + propValue;
});
/*
obj will now be:
var obj = {
a: 'a-foo',
b: 'b-bar',
c: 'c-baz'
};
*/