Is there a way to clone a javascript object? Not a DOM element, but an object with a specific constructor. For instance lets say I have a phone number object:
function PhoneNumber(number, type) {
this.number = number;
this.type = type;
}
var phoneObj1 = new PhoneNumber('1111111111', 'Home');
var phoneObj2 = //Copy of phoneObj1, but unique so I can set other parameters if necessary.
jQuery solves this problem for me with extend.
var phoneObj2 = $.extend({}, phoneObj1);
But it returns a generic object:
Object{number:'1111111111' type:'Home'}
Instead of with the Phone Number constructor name. I need a way of passing in the original phoneObj1's constructor without actually writing new PhoneNumber() in the extend, because the code this is being used in is used by many different types of Objects so at any given time I don't know the exact constructor that should be used except for the fact that I have an object to reference.
var phoneObj2 = $.extend(new PhoneNumber(), phoneObj1); //I CANNOT do this!
//Maybe something like this?
var phoneObj2 = $.extend(new phoneObj1.constructor.name, phoneObj1); //This throws an error.
You could have a method (perhaps called copy()) on each of your different types of objects that either returns the constructor for that type of object or just makes a copy of itself (calling the right constructor, since it knows what kind of object it is). So, you just call the same method on all the objects and it does the work for you.
You could put this functionality in a base class and all each derived class would have to do is set the constructor function and all the rest of the code would be common.
I you only need to support modern version of chrome (45) or firefox (34) you can use the new ecmascript-6 Object.assign() method.
The Object.assign() method is used to copy the values of all
enumerable own properties from one or more source objects to a target
object. It will return the target object.
Example
var phoneObj2 = Object.assign({}, phoneObj1);
console.log(phoneObj2);
This will take all of the properties in phoneObj1, copy them to the empty object {}, and return the combined object as phoneObj2. The empty {} object and the phoneObj2 variable point to the same object in memory after using the assign method.
For more information checkout the info on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Turns out that
var phoneObj2 = $.extend(new phoneObj1.constructor, phoneObj1);
Does work. I was just cancelling the constructor of phoneObj1 on an unrelated note. So this can be used as an abstract way of cloning an object without specifically writing the actual constructor. I can simply inherit the constructor from the object I am cloning.
Checkout the full post ->
https://jscurious.com/how-to-deep-clone-objects-in-javascript/
const obj = {
name: 'Amitav',
age: 24,
address: {
city: 'Bangalore',
state: 'Karnataka'
}
};
const makeDeepClone = (obj) => {
let newObject = {};
Object.keys(obj).map(key => {
if(typeof obj[key] === 'object'){
newObject[key] = makeDeepClone(obj[key]);
} else {
newObject[key] = obj[key];
}
});
return newObject;
}
const copyObj = makeDeepClone(obj);
console.log(copyObj);
// {name: "Amitav", age: 24, address: {city: "Bangalore", state: "Karnataka"}}
copyObj.address.state = 'Odisha';
console.log(obj.address.state); // "Karnataka"
console.log(copyObj.address.state); // "Odisha"
Related
I'm having some difficulty trying to write a function that takes two inputs:
The name of an object
The name of a property
and prints the value of that property for that object. However, the objects all have different properties, they're not all the same.
The objects look like this:
class object1 {
get property1() {
return 'foo';
}
get property2() {
return 'bar';
}
}
export default new object1();
class object2 {
get different1() {
return 'asdf';
}
get different2() {
return 'ghjk';
}
}
export default new object2();
Here's what I'm tried so far:
import object1 from '..';
import object2 from '..';
getPropertValue(objectName, propertyName) {
let objects = [object1, object2];
let index = objects.indexOf(objectName);
console.log(objects[index][propertyName]);
}
This didn't work, came back as undefined. It seems like index is being calculated correctly, but it doesn't seem like objects[index][propertyName] is properly accessing the object's value. Though weirdly, when I tried the following, it ALMOST worked:
import object1 from '..';
import object2 from '..';
getPropertValue(objectName, propertyName) {
let objects = [object1, object2];
for (let index in objects) {
console.log(objects[index][propertyName]);
}
}
Here I actually got it to print the correct value, but the problem is that since I'm just iterating over all the objects in a for loop, it tries to print the value for all objects, instead of just the one that matches the objectName. So it prints the correct value for the object that has the property I'm trying to access, but then gets undefined for the other object which does not have that property.
I suppose I could add some property to each object called name and do something like this:
getPropertValue(objectName, propertyName) {
let objects = [object1, object2];
for (let index in objects) {
if(objects[index].name == objectName) {
console.log(objects[index][propertyName]);
}
}
}
But I'd rather not add unnecessary properties to these objects if I can help it.
Is there a better way to do this?
Objects can be referenced by a variable, but and object has no quality of being named unless your code explicitly provides that as part of your data model.
For instance:
const obj = { abc: 123 }
Here the value { abc: 123 } is not named obj. There is a variable with an identifier of obj that references the value that is { abc: 123 }.
What this means in practice is that if you only have a reference to { abc: 123 }, then you cannot know what other variable identifiers also hold a reference to this value.
And you cannot get a variable's value by its identifier as a string.
So this cannot possibly work:
const foo = 'bar'
function getVar(varName: string) {
// impossible function implementation here.
}
getVar('foo') // expected return value: 'bar'
But what you can have is an object with properties. Properties have keys which are specific strings.
const objects = { // note the {}, [] is for arrays
object1,
object2,
}
Which is shorthand for:
const objects = {
"object1": object1,
"object2": object2,
}
You now have object with properties that correspond to the names you want to use for your objects.
Lastly, let's type your function properly:
// store this outside the function
const objects = {
object1,
object2,
}
function getPropertyValue<
T extends keyof typeof objects
>(
objectName: T,
propertyName: keyof typeof objects[T]
) {
console.log(objects[objectName][propertyName]);
}
This function is now generic, which means it accepts a type. In this case it accepts the object name you want to use as T, which any keyof the objects object.
Then the propertyName argument uses whatever key that was to find out what properties should be allowed, by constraining the type to the properties of just one of those objects.
Now to get the data you drill into objects by the name of a property on objects, and then again on the specific property name.
Usage looks like this:
getPropertyValue('object1', 'property1') // 'foo'
getPropertyValue('object2', 'different1') // 'asdf'
getPropertyValue('object1', 'different1')
// Type error, 'different1' does not exist on 'object1'
See Playground
Object names
As Alex Wayne explains: Objects will only have names "as part of your data model."
Or in other words: An object's name is what you associate it with.
That association may come from within or from outside. Example:
// Name by self-definition
const someObject = { name: "self-named" };
// Name by association
const nameToObject = { "associated-name": {} };
Looking up by name
To avoid adding unnecessary properties to your objects, I'll use the "name by association" approach:
const objects = { object1, object2 };
Sidenote: In this shorthand object initialization, the identifiers are used as property names and their references as the properties' values.
With this, an implementation is straightforward:
const object1 = new class Object1 {
get property1() { return "foo"; }
get property2() { return "bar"; }
};
const object2 = new class Object1 {
get different1() { return "asdf"; }
get different2() { return "ghjk"; }
};
const objects = { object1, object2 };
// The implementation
function getPropertyValue(objectName, propertyName) {
return objects[objectName][propertyName];
}
const value = getPropertyValue("object1", "property2");
console.log(value);
Adding Typescript
To add proper typing, we need to think about how the output depends on the inputs. But also, how the inputs depend on each other.
A naive implementation may look like this:
function getPropertyValue(
objectName: "object1" | "object2",
propertyName: "property1" | "property2" | "different1" | "different2"
): any;
But this doesn't fully use what Typescript is capable of. Only a subset of propertyName's original types can be used depending on objectName's value: propertyName depends on objectName.
Since there is one dependency between function parameters, we need a generic parameter to link them.
We already know which value of objectName corresponds to which type, thanks to our dictionary. We only need to restrict propertyName to that type's keys:
type Objects = typeof objects;
function getPropertyValue<
K extends keyof Objects
>(
objectName: K,
propertyName: keyof Objects[K]
);
When calling this function, K will be inferred from objectName by default, and automatically restrict our options for propertyName according to our typing.
Addendum
Name by identifier?
While (especially const declared) variables can reference objects, their identifier –that is the variable name– is usually not considered their referenced object's name. For good reason!
You cannot easily look up objects through an identifier. To do so requires either new Function or eval(), which are known for having security issues.
Therefore, please see the following as a demonstration, not recommendation:
const object1 = { name: "Object-1" };
const object2 = { name: "Object-2" };
const o = getObjectByName("object1");
console.log(o);
function getObjectByName(name) {
// You should avoid new Function!
return new Function(`return ${name};`)();
}
for...in, for...of, what for?
There are multiple for loops:
The regular (or simple) for-loop with an initialization, condition and incrementation block.
The for...of-loop to iterate through an iterable object.
The for...in-loop to loop over enumerable properties.
Make sure you familiarize yourself with these!
Tip: In many cases you can use Object.keys() or .entries() instead of a for...in-loop, which avoids confusion with for...of-loops.
Namespace pollution!
We don't want to instantiate objects for every function call, which means it needs to be outside that function.
But naively moving it outside pollutes the namespace.
Instead, we can make use of a closure to instantiate it only once, but keep it hidden from the global scope:
// Mock imports
const object1 = { property1: "foo", property2: "bar" };
const object2 = { different1: "asdf", different2: "ghjk" };
const getPropertyValue = (() => {
const objects = { object1, object2 };
return function(objectName, propertyName) {
return objects[objectName][propertyName];
};
})();
const value = getPropertyValue("object1", "property2");
console.log(value);
console.log(objects);
I have two objects inst1, inst2 which are both instances of the same class. If I use
inst2 = JSON.parse(JSON.stringify(inst1));
now if I change values of properties of inst2, values in inst1 do not change. That is great.
But sadly methods of inst2 have disappeared. So if I do
inst2.method1();
I get the error
"inst2.method1 is not a function"
Is there some way I can copy the values in an instance without destroying methods?
(obviously I could laboriously copy each value. I am trying to avoid that because I am lazy.)
I have tried to follow typescript - cloning object but I cannot make it work-
Ok, I have played a little since the provided answers are not 100% clear.
If you want to have a shallow copy and copy the methods too, you can use Object.create.
Again: If your object is simple enough, Object.create will be sufficient for you
const originalPerson = new Person("John");
originalPerson.address = new Address("Paris", "France");
const newPerson = Object.create(originalPerson);
/// this will be true
const isInstanceOf = newPerson instanceof Person;
//this will change the property of the new person ONLY
newPerson.name = "Peter";
//methods will work
newPerson.someMethod();
//methods will work even on nested objects instances
newPerson.address.anotherMethod();
// BUT if we change the city on any of the instances - will change the address.city of both persons since we have done a shallow copy
newPerson.address.city = "Berlin";
I have created typescript playground (just remove the types) to show it works and the drawback with its usage - link to the playground
Another approach is the class itself to have a clone method and to be responsible for its own cloning logic. An example follows, along with a link to another playground
class Address {
constructor(city, country) {
this.city = city;
this.country = country;
}
clone() {
// no special logic, BUT if the address eveolves this is the place to change the clone behvaiour
return Object.create(this);
}
getAddressDetails() {
return `City: ${this.city} country ${this.country}`;
}
}
class Person {
constructor(name, address) {
this.name = name;
this.address = address;
}
clone() {
const newInstance = Object.create(this);
//clone all other class instances
newInstance.address = this.address.clone();
return newInstance;
}
getPersonDetails() {
//calling internally address.getAddressDetails() ensures that the inner object methods are also cloned
return `This is ${this.name}, I live in ${this.address.getAddressDetails()}`
}
}
const originalAddress = new Address("Paris", "France");
const originalPerson = new Person("John", originalAddress);
const clonedPerson = originalPerson.clone();
clonedPerson.name = "Peter";
clonedPerson.address.city = "Berlin";
clonedPerson.address.country = "Germany";
// Log to console
console.log(`Original person: ${originalPerson.getPersonDetails()}`)
console.log(`Cloned person: ${clonedPerson.getPersonDetails()}`)
You should use structured cloning, see this answer:
How do I correctly clone a JavaScript object?
The reason that your current code isn't working is because you are parsing a stringified json object. Json stringify will remove all of the methods of an object and only stringify the objects values.
I came back to this at a convenient point and made quite a bit of progress by combining some of the above answers. The general purpose cloner was getting quite ugly (see below) and still not working (for arrays of class-objects) when I realised that it would be impossible to write a general purpose cloner.
I use the term class-object to mean an object defined by a class.
If a class-object contains a variable which itself is type class-object, call it subObj, then the general purpose cloner cannot know whether 1) it should copy subObj or 2) it should create a new instance of subObj and copy into the sub-properties. The answer depends on the meaning in the class.
In the first case above subObj. is just a pointer to another instance of subObj.
Therefore I strongly agree with the second part of Svetoslav Petkov's answer that the "class itself [should] have a clone method and be responsible for its own cloning logic.".
For what it's worth this is as far as I got with a general purpose cloner (in TypeScript). It is adapted from the other answers and creates new instances of class-objects liberally:
public clone(): any {
var cloneObj = new (this.constructor as any)() as any;
for (var attribut in this) {
// attribut is a string which will take the values of the names of the propertirs in 'this'
// And for example, if aNumber is a property of 'this' then
// this['aNumber'] is the same as this.aNumber
if (typeof this[attribut] === "object") {
let thisAttr = this[attribut] as any;
let cloneAttr = cloneObj[attribut] as any;
if (this[attribut] instanceof Array) {
for (let i in thisAttr) {
cloneAttr[i] = thisAttr[i]; // **** will not work on arrays of objects!!
}
continue; // to next attrib in this
}
if (this[attribut] instanceof Date) {
cloneAttr.setTime(thisAttr.getTime());
continue; // to next attrib in this
}
try {
cloneObj[attribut] = thisAttr.clone();
//cloneObj[attribut] = this.clone(); // with this, (from https://stackoverflow.com/questions/28150967/typescript-cloning-object) stack just gets bigger until overflow
}
catch (err) {
alert("Error: Object " + attribut + " does not have clone method." +
"\nOr " + err.message);
}
} else {
cloneObj[attribut] = this[attribut];
}
}
return cloneObj;
}
I'm using a library that turns things into ES6 Proxy objects, and another library that I think is choking because I'm passing it one of these (my code is a travesty, I know), and I couldn't figure out how to unProxy the Proxy object.
But I was just being dumb. Proxies can do anything!
I find a hack. In my case, I can't control the creation of the proxy(mobx observable values). So the solution is:
JSON.parse(JSON.stringify(your.object))
Tried using JSON.parse(JSON.stringify(proxyObj)) but that removes anything that cannot be stringified (like classes, functions, callback, etc...) which isn't good for my use case since I have some callback function declarations in my object and I want to see them as part of my object. However I found out that using the Lodash cloneDeep function does a real good job at converting a Proxy object into a POJO while keeping the object structure.
convertProxyObjectToPojo(proxyObj) {
return _.cloneDeep(proxyObj);
}
How about using the Spread Operator?
const plainObject = { ...proxyObject };
pp = new Proxy(
{a:1},
{
get: function(target, prop, receiver) {
if(prop==='target') return target
}
}
)
But that will only work if you can control creation of the proxy. It turns out to be even easier though:
pojo = Object.assign({}, proxyObj) // won't unwrap nested proxies though
For readers who might like this answer, David Gilbertson's newer answer might be better. I personally prefer lodash clonedeep. And the most popular seems to be JSON.parse(JSON.stringify(...))
If you don't want to use lodash The object.assign({},val) method is OK. But if the val contains nested objects they will be proxified. So it has to be done recursively like that:
function unproxify(val) {
if (val instanceof Array) return val.map(unproxify)
if (val instanceof Object) return Object.fromEntries(Object.entries(Object.assign({},val)).map(([k,v])=>[k,unproxify(v)]))
return val
}
In fact it is a deep clone function. I think that it does not work if val contains Map objects but you can modify the function for that following the same logic.
If you want to unproxify a Vue3 proxy, Vue3 provides a function : toRaw
Thank you #sigfried for the answer, that's exactly what I was looking for!
Here's a slightly more detailed version, using Symbol to avoid clashes with real prop names.
const original = {foo: 'bar'};
const handler = {
get(target, prop) {
if (prop === Symbol.for('ORIGINAL')) return target;
return Reflect.get(target, prop);
}
};
const proxied = new Proxy(original, handler);
console.assert(original !== proxied);
const unproxied = proxied[Symbol.for('ORIGINAL')];
console.assert(original === unproxied);
Normally you'd use mobx util toJS() for this.
import { observable, toJS } from "mobx";
const observed = observable({ foo: 'bar' })
const obj = toJS(observed)
Assuming you don't have access to the original target, the quickest way to convert a Proxy is to assign its values to a new plain Object:
const obj = Object.assign({}, proxy);
Or use a spreader:
const obj = { ...proxy };
Example
const user = {
firstName: 'John',
lastName: 'Doe',
email: 'john.doe#example.com',
};
const handler = {
get(target, property) {
return target[property];
}
};
const proxy = new Proxy(user, handler);
const a = Object.assign({}, proxy);
console.log(`Using Object.assign():`);
console.log(`Hello, ${a.firstName} ${a.lastName}!`);
console.log(JSON.stringify(a));
const s = { ...proxy };
console.log(`Using spreader:`);
console.log(`Hello, ${s.firstName} ${s.lastName}!`);
console.log(JSON.stringify(s));
In the unproxied object constructor, add this.self=this
Then make sure your get handler allows the self property to be returned and you are all set. proxiedMyObj.self===myObj //returns true
I would like to add key-value pairs of metadata to arbitrary JavaScript objects. This metadata should not affect code that is not aware of the metadata, that means for example
JSON.stringify(obj) === JSON.stringify(obj.WithMetaData('key', 'value'))
MetaData aware code should be able to retrieve the data by key, i.e.
obj.WithMetaData('key', 'value').GetMetaData('key') === 'value'
Is there any way to do it - in node.js? If so, does it work with builtin types such as String and even Number? (Edit Thinking about it, I don't care about real primitives like numbers, but having that for string instances would be nice).
Some Background: What I'm trying to do is cache values that are derived from an object with the object itself, so that
to meta data unaware code, the meta data enriched object will look the same as the original object w/o meta
code that needs the derived values can get it out of the meta-data if already cached
the cache will get garbage collected alongside the object
Another way would be to store a hash table with the caches somewhere, but you'd never know when the object gets garbage collected. Every object instance would have to be taken care of manually, so that the caches don't leak.
(btw clojure has this feature: http://clojure.org/metadata)
You can use ECMA5's new object properties API to store properties on objects that will not show up in enumeration but are nonetheless retrievable.
var myObj = {};
myObj.real_property = 'hello';
Object.defineProperty(myObj, 'meta_property', {value: 'some meta value'});
for (var i in myObj)
alert(i+' = '+myObj[i]); //only one property - #real_property
alert(myObj.meta_property); //"some meta value"
More information here: link
However you're not going to be able to do this on primitive types such as strings or numbers, only on complex types.
[EDIT]
Another approach might be to utilise a data type's prototype to store meta. (Warning, hack ahead). So for strings:
String.prototype.meta = {};
String.prototype.addMeta = function(name, val) { this.meta[name] = val; }
String.prototype.getMeta = function(name) { return this.meta[name]; };
var str = 'some string value';
str.addMeta('meta', 'val');
alert(str.getMeta('meta'));
However this is clearly not ideal. For one thing, if the string was collected or aliased (since simple data types are copied by value, not reference) you would lose this meta. Only the first approach has any mileage in a real-world environment, to be honest.
ES6 spec introduces Map and WeakMap. You can enable these in node by running node --harmony and by enabling the experimental javascript flag in Chrome, (it's also in Firefox by default). Maps and WeakMaps allow objects to be used as keys which can be be used to store metadata about objects that isn't visible to anyone without access to the specific map/weakmap. This is a pattern I now use a lot:
function createStorage(creator){
creator = creator || Object.create.bind(null, null, {});
var map = new Map;
return function storage(o, v){
if (1 in arguments) {
map.set(o, v);
} else {
v = map.get(o);
if (v == null) {
v = creator(o);
map.set(o, v);
}
}
return v;
};
}
Use is simple and powerful:
var _ = createStorage();
_(someObject).meta= 'secret';
_(5).meta = [5];
var five = new Number(5);
_(five).meta = 'five';
console.log(_(someObject).name);
console.log(_(5).meta);
console.log(_(five).meta);
It also facilitates some interesting uses for separating implementation from interface:
var _ = createStorage(function(o){ return new Backing(o) });
function Backing(o){
this.facade = o;
}
Backing.prototype.doesStuff = function(){
return 'real value';
}
function Facade(){
_(this);
}
Facade.prototype.doSomething = function doSomething(){
return _(this).doesStuff();
}
There is no "comment" system in JSON. The best you can hope for is to add a property with an unlikely name, and add that key contaning the metadata. You can then read the metadata back out if you know it's metadata, but other setups will just see it as another property. And if someone uses for..in...
You could just add the Metadata as a "private" variable!?
var Obj = function (meta) {
var meta = meta;
this.getMetaData = function (key) {
//do something with the meta object
return meta;
};
};
var ins_ob = new Obj({meta:'meta'});
var ins_ob2 = new Obj();
if(JSON.stringify(ins_ob) === JSON.stringify(ins_ob2)) {
console.log('hoorai');
};
If you want object-level metadata, you could create a class that extends Object. Getters and setters are not enumerable and, obviously, neither are private fields.
class MetadataObject extends Object {
#metadata = undefined;
get metadata() { return this.#metadata; }
set metadata(value) { this.#metadata; }
}
var obj = new MetadataObject();
obj.a = 1;
obj.b = 2;
obj.metadata = { test: 123 };
console.log(obj); // { a: 1, b: 2 }
console.log(obj.metadata); // { test: 123 }
console.log(JSON.stringify(obj)); // '{"a":1,"b":2}'
You can even simplify the implementation using a Map. Without a setter on metadata, you have to use Map methods to modify it.
class MetadataObject extends Object {
#metadata = new Map();
get metadata() { return this.#metadata; }
}
var obj = new MetadataObject();
obj.a = 1;
obj.b = 2;
obj.metadata.set('test', 123);
console.log(obj); // { a: 1, b: 2 }
console.log(obj.metadata.get('test')); // 123
console.log(JSON.stringify(obj)); // '{"a":1,"b":2}'
I ran into a situation where I needed property level metadata, and used the latter implementation.
obj.id = 1;
obj.metadata.set('id', 'metadata for the id property');
If you have an array of product objects created from JSON, how would you add a prototype method to the product objects so that they all point to the same method? How would you train JavaScript to recognize all product objects in an array are instances of the same class without recreating them?
If I pull down a JSON array of Products for example, and want each product in the array to have a prototype method, how would I add the single prototype method to each copy of Product?
I first thought to have a Product constructor that takes product JSON data as a parameter and returns a new Product with prototypes, etc. which would replace the data send from the server. I would think this would be impractical because you are recreating the objects. We just want to add functions common to all objects.
Is it possible to $.extend an object's prototype properties to the JSON object so that each JSON object would refer to exactly the same functions (not a copy of)?
For example:
var Products = [];
Products[0] = {};
Products[0].ID = 7;
Products[0].prototype.GetID = function() { return this.ID; };
Products[1].ID = 8;
Products[1].prototype = Products[0].prototype; // ??
I know that looks bad, but what if you JQuery $.extend the methods to each Product object prototype: create an object loaded with prototypes then $.extend that object over the existing Product objects? How would you code that? What are the better possibilities?
For one, you're not modifying the Products[0].prototype, you're modifying Object.prototype, which will put that function on the prototype of all objects, as well as making it enumerable in every for loop that touches an Object.
Also, that isn't the proper way to modify a prototype, and ({}).prototype.something will throw a TypeError as .prototype isn't defined. You want to set it with ({}).__proto__.something.
If you want it to be a certain instance you need to create that instance, otherwise it will be an instance of Object.
You probably want something like:
var Product = function(ID) {
if (!this instanceof Product)
return new Product(ID);
this.ID = ID;
return this;
};
Product.prototype.GetID = function() {
return this.ID;
};
Then, fill the array by calling new Product(7) or whatever the ID is.
First, one problem is that prototype methods are associated when the object is created, so assigning to an object's prototype will not work:
var Products = [];
Products[0] = {};
Products[0].prototype.foo = function () { return 'hello' } // ***
Products[0].foo(); // call to undefined function
(*** Actually, the code fails here, because prototype is undefined.)
So in order to attach objects, you'll need to assign actual functions to the object:
Products[0].foo = function () { return 'hello'; };
You can create a helper function to do so:
var attachFoo = (function () { // Create a new variable scope, so foo and
// bar is not part of the global namespace
function foo() { return this.name; }
function bar() { return 'hello'; }
return function (obj) {
obj.foo = foo;
obj.bar = bar;
return obj; // This line is actually optional,
// as the function /modifies/ the current
// object rather than creating a new one
};
}());
attachFoo(Products[0]);
attachFoo(Products[1]);
// - OR -
Products.forEach(attachFoo);
By doing it this way, your obj.foos and obj.bars will all be referencing the same foo() and bar().
So, if I'm getting this all correctly, this is a more complete example of KOGI's idea:
// Create a person class
function Person( firstName, lastName ) {
var aPerson = {
firstName: firstName,
lastName: lastName
}
// Adds methods to an object to make it of type "person"
aPerson = addPersonMethods( aPerson );
return aPerson;
}
function addPersonMethods( obj ) {
obj.nameFirstLast = personNameFirstLast;
obj.nameLastFirst = personNameLastFirst;
return obj;
}
function personNameFirstLast() {
return this.firstName + ' ' + this.lastName;
}
function personNameLastFirst() {
return this.lastName + ', ' + this.firstName;
}
So, with this structure, you are defining the methods to be added in the addPersonMethods function. This way, the methods of an object are defined in a single place and you can then do something like this:
// Given a variable "json" with the person json data
var personWithNoMethods = JSON.parse( json ); // Use whatever parser you want
var person = addPersonMethods( personWithNoMethods );
You could do this...
function product( )
{
this.getId = product_getId;
// -- create a new product object
}
function product_getId( )
{
return this.id;
}
This way, although you will have several instances of the product class, they all point to the instance of the function.
Could try doing something like this (without jquery)
Basic prototypal object:
function Product(id){
this.id = id;
}
Product.prototype.getId() = function(){return this.id;};
var Products = [];
Products[0] = new Product(7);
Products[1] = new Product(8);
Products[2] = new Product(9);
alert(Products[2].getId());
IMO I found a pretty good answer right here:
Return String from Cross-domain AJAX Request
...I could serialize my
data in the service as a JSON string
and then further wrap that in JSONP
format? I guess when it comes over to
the client it would give the JSON
string to the callback function.
That's not a bad idea. I guess I would
also have the option of sending a
non-JSON string which might allow me
to just use eval in the callback
function to create new Person objects.
I'm thinking this would be a more
efficient solution in both speed and
memory usage client-side.